QmlJS: Lazy-load console items to allow for recursion

Using Utils:TreeView automatically gives us the capability for loading
item as they are expanded. This way we can show recursive structure in
the console as well as load data from the debug server on demand.

Also, properly print error messages received from unsuccessful
command evaluations.

Task-number: QTCREATORBUG-14931
Change-Id: I66d440eedd9723b04670169b27db1ee18f3f2891
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
Ulf Hermann
2015-08-21 17:33:53 +02:00
parent 0b32832812
commit b5717a5315
15 changed files with 354 additions and 494 deletions

View File

@@ -215,10 +215,10 @@ void QmlConsoleEdit::handleUpKey()
if (model->hasIndex(currentRow, 0)) {
QModelIndex index = model->index(currentRow, 0);
if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data(
index, QmlConsoleItemModel::TypeRole).toInt()) {
index, ConsoleItem::TypeRole).toInt()) {
m_historyIndex = index;
replaceCurrentScript(
model->data(index, QmlConsoleItemModel::ExpressionRole).toString());
model->data(index, ConsoleItem::ExpressionRole).toString());
break;
}
}
@@ -235,13 +235,13 @@ void QmlConsoleEdit::handleDownKey()
if (model->hasIndex(currentRow, 0)) {
QModelIndex index = model->index(currentRow, 0);
if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data(
index, QmlConsoleItemModel::TypeRole).toInt()) {
index, ConsoleItem::TypeRole).toInt()) {
m_historyIndex = index;
if (currentRow == model->rowCount() - 1) {
replaceCurrentScript(m_cachedScript);
} else {
replaceCurrentScript(
model->data(index, QmlConsoleItemModel::ExpressionRole).toString());
model->data(index, ConsoleItem::ExpressionRole).toString());
}
break;
}

View File

@@ -92,7 +92,7 @@ QColor QmlConsoleItemDelegate::drawBackground(QPainter *painter, const QRect &re
{
painter->save();
ConsoleItem::ItemType itemType = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
QColor backgroundColor;
switch (itemType) {
case ConsoleItem::DebugType:
@@ -138,7 +138,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
QColor textColor;
QIcon taskIcon;
ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
switch (type) {
case ConsoleItem::DebugType:
textColor = QColor(CONSOLE_LOG_TEXT_COLOR);
@@ -175,7 +175,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
}
int width = view->width() - level * view->indentation() - view->verticalScrollBar()->width();
bool showTypeIcon = index.parent() == QModelIndex();
bool showExpandableIcon = type == ConsoleItem::UndefinedType;
bool showExpandableIcon = type == ConsoleItem::DefaultType;
QRect rect(opt.rect.x(), opt.rect.top(), width, opt.rect.height());
ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon);
@@ -206,7 +206,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
if (showExpandableIcon) {
// Paint ExpandableIconArea:
QIcon expandCollapseIcon;
if (index.model()->rowCount(index)) {
if (index.model()->rowCount(index) || index.model()->canFetchMore(index)) {
if (view->isExpanded(index))
expandCollapseIcon = m_collapseIcon;
else
@@ -219,7 +219,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
if (showFileLineInfo) {
// Check for file info
QString file = index.data(QmlConsoleItemModel::FileRole).toString();
QString file = index.data(ConsoleItem::FileRole).toString();
const QUrl fileUrl = QUrl(file);
if (fileUrl.isLocalFile())
file = fileUrl.toLocalFile();
@@ -244,7 +244,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem
}
// Paint LineArea
QString lineText = index.data(QmlConsoleItemModel::LineRole).toString();
QString lineText = index.data(ConsoleItem::LineRole).toString();
painter->setClipRect(positions.lineArea());
const int realLineWidth = fm.width(lineText);
painter->drawText(positions.lineAreaRight() - realLineWidth,
@@ -277,9 +277,9 @@ QSize QmlConsoleItemDelegate::sizeHint(const QStyleOptionViewItem &option,
return QSize(width, m_cachedHeight);
ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
bool showTypeIcon = index.parent() == QModelIndex();
bool showExpandableIcon = type == ConsoleItem::UndefinedType;
bool showExpandableIcon = type == ConsoleItem::DefaultType;
QRect rect(level * view->indentation(), 0, width, 0);
ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon);
@@ -322,7 +322,7 @@ void QmlConsoleItemDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QmlConsoleEdit *edtr = qobject_cast<QmlConsoleEdit *>(editor);
edtr->insertPlainText(index.data(QmlConsoleItemModel::ExpressionRole).toString());
edtr->insertPlainText(index.data(ConsoleItem::ExpressionRole).toString());
}
void QmlConsoleItemDelegate::setModelData(QWidget *editor,
@@ -330,8 +330,8 @@ void QmlConsoleItemDelegate::setModelData(QWidget *editor,
const QModelIndex &index) const
{
QmlConsoleEdit *edtr = qobject_cast<QmlConsoleEdit *>(editor);
model->setData(index, edtr->getCurrentScript(), Qt::DisplayRole);
model->setData(index, ConsoleItem::InputType, QmlConsoleItemModel::TypeRole);
model->setData(index, edtr->getCurrentScript(), ConsoleItem::ExpressionRole);
model->setData(index, ConsoleItem::InputType, ConsoleItem::TypeRole);
}
void QmlConsoleItemDelegate::updateEditorGeometry(QWidget *editor,

View File

@@ -45,89 +45,55 @@ namespace Internal {
///////////////////////////////////////////////////////////////////////
QmlConsoleItemModel::QmlConsoleItemModel(QObject *parent) :
QAbstractItemModel(parent),
m_hasEditableRow(false),
m_rootItem(new ConsoleItem(0)),
Utils::TreeModel(new ConsoleItem, parent),
m_maxSizeOfFileName(0)
{
}
QmlConsoleItemModel::~QmlConsoleItemModel()
{
delete m_rootItem;
clear();
}
void QmlConsoleItemModel::clear()
{
beginResetModel();
delete m_rootItem;
m_rootItem = new ConsoleItem(0);
endResetModel();
if (m_hasEditableRow)
appendEditableRow();
Utils::TreeModel::clear();
appendItem(new ConsoleItem(ConsoleItem::InputType));
emit selectEditableRow(index(0, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect);
}
bool QmlConsoleItemModel::appendItem(ConsoleItem *item, int position)
void QmlConsoleItemModel::appendItem(ConsoleItem *item, int position)
{
if (position < 0)
position = m_rootItem->childCount() - 1;
position = rootItem()->childCount() - 1; // append before editable row
if (position < 0)
position = 0;
beginInsertRows(QModelIndex(), position, position);
bool success = m_rootItem->insertChild(position, item);
endInsertRows();
return success;
rootItem()->insertChild(position, item);
}
bool QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType,
void QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType,
const QString &message, int position)
{
return appendItem(new ConsoleItem(m_rootItem, itemType, message), position);
appendItem(new ConsoleItem(itemType, message), position);
}
void QmlConsoleItemModel::setHasEditableRow(bool hasEditableRow)
void QmlConsoleItemModel::shiftEditableRow()
{
if (m_hasEditableRow && !hasEditableRow)
removeEditableRow();
int position = rootItem()->childCount();
Q_ASSERT(position > 0);
if (!m_hasEditableRow && hasEditableRow)
appendEditableRow();
// Disable editing for old editable row
rootItem()->lastChild()->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
m_hasEditableRow = hasEditableRow;
}
bool QmlConsoleItemModel::hasEditableRow() const
{
return m_hasEditableRow;
}
void QmlConsoleItemModel::appendEditableRow()
{
int position = m_rootItem->childCount();
if (appendItem(new ConsoleItem(m_rootItem, ConsoleItem::InputType), position))
emit selectEditableRow(index(position, 0), QItemSelectionModel::ClearAndSelect);
}
void QmlConsoleItemModel::removeEditableRow()
{
if (m_rootItem->child(m_rootItem->childCount() - 1)->itemType == ConsoleItem::InputType)
removeRow(m_rootItem->childCount() - 1);
appendItem(new ConsoleItem(ConsoleItem::InputType), position);
emit selectEditableRow(index(position, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect);
}
int QmlConsoleItemModel::sizeOfFile(const QFont &font)
{
int lastReadOnlyRow = m_rootItem->childCount();
if (m_hasEditableRow)
lastReadOnlyRow -= 2;
else
lastReadOnlyRow -= 1;
int lastReadOnlyRow = rootItem()->childCount();
lastReadOnlyRow -= 2; // skip editable row
if (lastReadOnlyRow < 0)
return 0;
QString filename = m_rootItem->child(lastReadOnlyRow)->file;
QString filename = static_cast<ConsoleItem *>(rootItem()->child(lastReadOnlyRow))->file();
const int pos = filename.lastIndexOf(QLatin1Char('/'));
if (pos != -1)
filename = filename.mid(pos + 1);
@@ -144,141 +110,5 @@ int QmlConsoleItemModel::sizeOfLineNumber(const QFont &font)
return fm.width(QLatin1String("88888"));
}
QVariant QmlConsoleItemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
ConsoleItem *item = getItem(index);
if (role == Qt::DisplayRole )
return item->text();
else if (role == QmlConsoleItemModel::TypeRole)
return int(item->itemType);
else if (role == QmlConsoleItemModel::FileRole)
return item->file;
else if (role == QmlConsoleItemModel::LineRole)
return item->line;
else if (role == QmlConsoleItemModel::ExpressionRole)
return item->expression();
else
return QVariant();
}
QModelIndex QmlConsoleItemModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
if (column > 0)
return QModelIndex();
ConsoleItem *parentItem = getItem(parent);
ConsoleItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex QmlConsoleItemModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
ConsoleItem *childItem = getItem(index);
ConsoleItem *parentItem = childItem->parent();
if (parentItem == m_rootItem)
return QModelIndex();
if (!parentItem)
return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem);
}
int QmlConsoleItemModel::rowCount(const QModelIndex &parent) const
{
ConsoleItem *parentItem = getItem(parent);
return parentItem->childCount();
}
int QmlConsoleItemModel::columnCount(const QModelIndex & /* parent */) const
{
return 1;
}
Qt::ItemFlags QmlConsoleItemModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
ConsoleItem *item = getItem(index);
if (m_hasEditableRow && item->parent() == m_rootItem
&& index.row() == m_rootItem->childCount() - 1)
return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
bool QmlConsoleItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
ConsoleItem *item = getItem(index);
bool result = false;
if (role == Qt::DisplayRole) {
item->setText(value.toString());
result = true;
} else if (role == QmlConsoleItemModel::TypeRole) {
item->itemType = (ConsoleItem::ItemType)value.toInt();
result = true;
} else if (role == QmlConsoleItemModel::FileRole) {
item->file = value.toString();
result = true;
} else if (role == QmlConsoleItemModel::LineRole) {
item->line = value.toInt();
result = true;
}
if (result)
emit dataChanged(index, index);
return result;
}
bool QmlConsoleItemModel::insertRows(int position, int rows, const QModelIndex &parent)
{
ConsoleItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows);
endInsertRows();
return success;
}
bool QmlConsoleItemModel::removeRows(int position, int rows, const QModelIndex &parent)
{
ConsoleItem *parentItem = getItem(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
ConsoleItem *QmlConsoleItemModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
ConsoleItem *item = static_cast<ConsoleItem*>(index.internalPointer());
if (item)
return item;
}
return m_rootItem;
}
} // Internal
} // QmlJSTools

View File

@@ -32,8 +32,8 @@
#define QMLCONSOLEITEMMODEL_H
#include <qmljs/consoleitem.h>
#include <utils/treemodel.h>
#include <QAbstractItemModel>
#include <QItemSelectionModel>
QT_FORWARD_DECLARE_CLASS(QFont)
@@ -41,58 +41,29 @@ QT_FORWARD_DECLARE_CLASS(QFont)
namespace QmlJSTools {
namespace Internal {
class QmlConsoleItemModel : public QAbstractItemModel
class QmlConsoleItemModel : public Utils::TreeModel
{
Q_OBJECT
public:
enum Roles { TypeRole = Qt::UserRole, FileRole, LineRole, ExpressionRole };
explicit QmlConsoleItemModel(QObject *parent = 0);
~QmlConsoleItemModel();
void setHasEditableRow(bool hasEditableRow);
bool hasEditableRow() const;
void appendEditableRow();
void removeEditableRow();
void shiftEditableRow();
bool appendItem(QmlJS::ConsoleItem *item, int position = -1);
bool appendMessage(QmlJS::ConsoleItem::ItemType itemType, const QString &message,
void appendItem(QmlJS::ConsoleItem *item, int position = -1);
void appendMessage(QmlJS::ConsoleItem::ItemType itemType, const QString &message,
int position = -1);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int sizeOfFile(const QFont &font);
int sizeOfLineNumber(const QFont &font);
QmlJS::ConsoleItem *root() const { return m_rootItem; }
public slots:
void clear();
signals:
void selectEditableRow(const QModelIndex &index, QItemSelectionModel::SelectionFlags flags);
void rowInserted(const QModelIndex &index);
protected:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex());
QmlJS::ConsoleItem *getItem(const QModelIndex &index) const;
private:
bool m_hasEditableRow;
QmlJS::ConsoleItem *m_rootItem;
int m_maxSizeOfFileName;
};

View File

@@ -57,7 +57,6 @@ QmlConsoleManager::QmlConsoleManager(QObject *parent)
d(new QmlConsoleManagerPrivate)
{
d->qmlConsoleItemModel = new Internal::QmlConsoleItemModel(this);
d->qmlConsoleItemModel->setHasEditableRow(true);
d->qmlConsolePane = new Internal::QmlConsolePane(this);
d->scriptEvaluator = 0;
ExtensionSystem::PluginManager::addObject(d->qmlConsolePane);
@@ -76,11 +75,6 @@ void QmlConsoleManager::showConsolePane()
d->qmlConsolePane->popup(Core::IOutputPane::ModeSwitch);
}
ConsoleItem *QmlConsoleManager::rootItem() const
{
return d->qmlConsoleItemModel->root();
}
void QmlConsoleManager::setScriptEvaluator(IScriptEvaluator *scriptEvaluator)
{
d->scriptEvaluator = scriptEvaluator;
@@ -109,7 +103,7 @@ void QmlConsoleManager::printToConsolePane(ConsoleItem *item, bool bringToForegr
{
if (!d->qmlConsolePane)
return;
if (item->itemType == ConsoleItem::ErrorType)
if (item->itemType() == ConsoleItem::ErrorType)
bringToForeground = true;
if (bringToForeground)
d->qmlConsolePane->popup(Core::IOutputPane::ModeSwitch);
@@ -118,47 +112,6 @@ void QmlConsoleManager::printToConsolePane(ConsoleItem *item, bool bringToForegr
namespace Internal {
ConsoleItem *constructLogItemTree(ConsoleItem *parent, const QVariant &result,
const QString &key = QString())
{
if (!result.isValid())
return 0;
ConsoleItem *item = new ConsoleItem(parent);
if (result.type() == QVariant::Map) {
if (key.isEmpty())
item->setText(QLatin1String("Object"));
else
item->setText(key + QLatin1String(" : Object"));
QMapIterator<QString, QVariant> i(result.toMap());
while (i.hasNext()) {
i.next();
ConsoleItem *child = constructLogItemTree(item, i.value(), i.key());
if (child)
item->insertChild(child, true);
}
} else if (result.type() == QVariant::List) {
if (key.isEmpty())
item->setText(QLatin1String("List"));
else
item->setText(QString::fromLatin1("[%1] : List").arg(key));
QVariantList resultList = result.toList();
for (int i = 0; i < resultList.count(); i++) {
ConsoleItem *child = constructLogItemTree(item, resultList.at(i),
QString::number(i));
if (child)
item->insertChild(child, true);
}
} else if (result.canConvert(QVariant::String)) {
item->setText(result.toString());
} else {
item->setText(QLatin1String("Unknown Value"));
}
return item;
}
QmlConsoleItemModel *QmlConsoleModel::qmlConsoleItemModel()
{
QmlConsoleManager *manager = qobject_cast<QmlConsoleManager *>(QmlConsoleManager::instance());
@@ -172,15 +125,15 @@ void QmlConsoleModel::evaluate(const QString &expression)
QmlConsoleManager *manager = qobject_cast<QmlConsoleManager *>(QmlConsoleManager::instance());
if (manager) {
if (manager->d->scriptEvaluator) {
QmlConsoleModel::qmlConsoleItemModel()->appendEditableRow();
QmlConsoleModel::qmlConsoleItemModel()->shiftEditableRow();
manager->d->scriptEvaluator->evaluateScript(expression);
} else {
ConsoleItem *root = manager->rootItem();
ConsoleItem *item = constructLogItemTree(
root, QCoreApplication::translate("QmlJSTools::Internal::QmlConsoleModel",
"Can only evaluate during a QML debug session."));
ConsoleItem *item = new ConsoleItem(
ConsoleItem::ErrorType, QCoreApplication::translate(
"QmlJSTools::Internal::QmlConsoleModel",
"Can only evaluate during a QML debug session."));
if (item) {
QmlConsoleModel::qmlConsoleItemModel()->appendEditableRow();
QmlConsoleModel::qmlConsoleItemModel()->shiftEditableRow();
manager->printToConsolePane(item);
}
}

View File

@@ -52,8 +52,6 @@ public:
void showConsolePane();
QmlJS::ConsoleItem *rootItem() const;
void setScriptEvaluator(QmlJS::IScriptEvaluator *scriptEvaluator);
void setContext(const QString &context);

View File

@@ -38,7 +38,7 @@ namespace Internal {
QmlConsoleProxyModel::QmlConsoleProxyModel(QObject *parent) :
QSortFilterProxyModel(parent),
m_filter(ConsoleItem::DefaultTypes)
m_filter(ConsoleItem::DefaultType | ConsoleItem::InputType)
{
}
@@ -72,7 +72,7 @@ bool QmlConsoleProxyModel::filterAcceptsRow(int sourceRow,
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
return m_filter.testFlag((ConsoleItem::ItemType)sourceModel()->data(
index, QmlConsoleItemModel::TypeRole).toInt());
index, ConsoleItem::TypeRole).toInt());
}
void QmlConsoleProxyModel::onRowsInserted(const QModelIndex &index, int start, int end)

View File

@@ -147,9 +147,9 @@ void QmlConsoleView::mousePressEvent(QMouseEvent *event)
QModelIndex index = indexAt(pos);
if (index.isValid()) {
ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data(
QmlConsoleItemModel::TypeRole).toInt();
ConsoleItem::TypeRole).toInt();
bool handled = false;
if (type == ConsoleItem::UndefinedType) {
if (type == ConsoleItem::DefaultType) {
bool showTypeIcon = index.parent() == QModelIndex();
ConsoleItemPositions positions(visualRect(index), viewOptions().font, showTypeIcon,
true);
@@ -222,14 +222,14 @@ void QmlConsoleView::onRowActivated(const QModelIndex &index)
return;
// See if we have file and line Info
QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString();
QString filePath = model()->data(index, ConsoleItem::FileRole).toString();
const QUrl fileUrl = QUrl(filePath);
if (fileUrl.isLocalFile())
filePath = fileUrl.toLocalFile();
if (!filePath.isEmpty()) {
QFileInfo fi(filePath);
if (fi.exists() && fi.isFile() && fi.isReadable()) {
int line = model()->data(index, QmlConsoleItemModel::LineRole).toInt();
int line = model()->data(index, ConsoleItem::LineRole).toInt();
Core::EditorManager::openEditorAt(fi.canonicalFilePath(), line);
}
}
@@ -240,15 +240,15 @@ void QmlConsoleView::copyToClipboard(const QModelIndex &index)
if (!index.isValid())
return;
QString contents = model()->data(index, QmlConsoleItemModel::ExpressionRole).toString();
QString contents = model()->data(index, ConsoleItem::ExpressionRole).toString();
// See if we have file and line Info
QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString();
QString filePath = model()->data(index, ConsoleItem::FileRole).toString();
const QUrl fileUrl = QUrl(filePath);
if (fileUrl.isLocalFile())
filePath = fileUrl.toLocalFile();
if (!filePath.isEmpty()) {
contents = QString::fromLatin1("%1 %2: %3").arg(contents).arg(filePath).arg(
model()->data(index, QmlConsoleItemModel::LineRole).toString());
model()->data(index, ConsoleItem::LineRole).toString());
}
QClipboard *cb = QApplication::clipboard();
cb->setText(contents);
@@ -260,7 +260,7 @@ bool QmlConsoleView::canShowItemInTextEditor(const QModelIndex &index)
return false;
// See if we have file and line Info
QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString();
QString filePath = model()->data(index, ConsoleItem::FileRole).toString();
const QUrl fileUrl = QUrl(filePath);
if (fileUrl.isLocalFile())
filePath = fileUrl.toLocalFile();