QML JS Inspector: Improve warning messages

When a change is done to an element that cannot be changed due to
debugger/qdeclarative limitations, a warning is shown to the user in QML
JS Editor.

Reviewed-by: Olivier Goffart
This commit is contained in:
Lasse Holmstedt
2010-08-05 16:22:30 +02:00
parent 1c2af44ad8
commit 714a266cbe
4 changed files with 104 additions and 29 deletions

View File

@@ -382,7 +382,7 @@ void Delta::update(UiObjectDefinition* oldObject, const QmlJS::Document::Ptr& ol
if (!previousScript || _scriptCode(previousScript, oldDoc) != scriptCode) { if (!previousScript || _scriptCode(previousScript, oldDoc) != scriptCode) {
foreach (DebugId ref, debugReferences) { foreach (DebugId ref, debugReferences) {
if (ref != -1) if (ref != -1)
updateScriptBinding(ref, script, property, scriptCode); updateScriptBinding(ref, newObject, script, property, scriptCode);
} }
} }
} else if (UiSourceElement *uiSource = cast<UiSourceElement*>(*it)) { } else if (UiSourceElement *uiSource = cast<UiSourceElement*>(*it)) {
@@ -393,7 +393,7 @@ void Delta::update(UiObjectDefinition* oldObject, const QmlJS::Document::Ptr& ol
if (!previousSource || _methodCode(previousSource, oldDoc) != methodCode) { if (!previousSource || _methodCode(previousSource, oldDoc) != methodCode) {
foreach (DebugId ref, debugReferences) { foreach (DebugId ref, debugReferences) {
if (ref != -1) if (ref != -1)
updateMethodBody(ref, script, methodName, methodCode); updateMethodBody(ref, newObject, script, methodName, methodCode);
} }
} }
} }
@@ -506,10 +506,10 @@ void Delta::removeObject(int)
{} {}
void Delta::resetBindingForObject(int, const QString &) void Delta::resetBindingForObject(int, const QString &)
{} {}
void Delta::updateMethodBody(DebugId, UiScriptBinding *, const QString &, const QString &) void Delta::updateMethodBody(DebugId, UiObjectDefinition *, UiScriptBinding *, const QString &, const QString &)
{} {}
void Delta::updateScriptBinding(DebugId, UiScriptBinding *, const QString &, const QString &) void Delta::updateScriptBinding(DebugId, UiObjectDefinition *, UiScriptBinding *, const QString &, const QString &)
{} {}
} //namespace QmlJs } //namespace QmlJs

View File

@@ -57,10 +57,12 @@ private:
protected: protected:
virtual void updateScriptBinding(DebugId objectReference, virtual void updateScriptBinding(DebugId objectReference,
AST::UiObjectDefinition *parentObject,
AST::UiScriptBinding *scriptBinding, AST::UiScriptBinding *scriptBinding,
const QString &propertyName, const QString &propertyName,
const QString &scriptCode); const QString &scriptCode);
virtual void updateMethodBody(DebugId objectReference, virtual void updateMethodBody(DebugId objectReference,
AST::UiObjectDefinition *parentObject,
AST::UiScriptBinding *scriptBinding, AST::UiScriptBinding *scriptBinding,
const QString &methodName, const QString &methodName,
const QString &methodBody); const QString &methodBody);

View File

@@ -422,50 +422,100 @@ private:
} }
protected: protected:
virtual void updateMethodBody(DebugId debugId, UiScriptBinding* scriptBinding, virtual void updateMethodBody(DebugId debugId,
UiObjectDefinition *parentDefinition, UiScriptBinding* scriptBinding,
const QString& methodName, const QString& methodBody) const QString& methodName, const QString& methodBody)
{ {
Q_UNUSED(scriptBinding); Q_UNUSED(scriptBinding);
checkUnsyncronizableElementChanges(parentDefinition);
appliedChangesToViewer = true;
ClientProxy::instance()->setMethodBodyForObject(debugId, methodName, methodBody); ClientProxy::instance()->setMethodBodyForObject(debugId, methodName, methodBody);
} }
virtual void updateScriptBinding(DebugId debugId, UiScriptBinding* scriptBinding, virtual void updateScriptBinding(DebugId debugId,
const QString& propertyName, const QString& scriptCode) UiObjectDefinition *parentDefinition, UiScriptBinding* scriptBinding,
const QString& propertyName, const QString& scriptCode)
{ {
if (propertyName == QLatin1String("id") && !hasUnsyncronizableChanges) { if (unsyncronizableChanges == QmlJSLiveTextPreview::NoUnsyncronizableChanges) {
hasUnsyncronizableChanges = true; if (propertyName == QLatin1String("id")) {
unsyncronizableChangeLine = scriptBinding->firstSourceLocation().startLine; unsyncronizableElementName = propertyName;
unsyncronizableChangeColumn = scriptBinding->firstSourceLocation().startColumn; unsyncronizableChanges = QmlJSLiveTextPreview::AttributeChangeWarning;
unsyncronizableChangeLine = parentDefinition->firstSourceLocation().startLine;
unsyncronizableChangeColumn = parentDefinition->firstSourceLocation().startColumn;
}
checkUnsyncronizableElementChanges(parentDefinition);
} }
QVariant expr = scriptCode; QVariant expr = scriptCode;
const bool isLiteral = isLiteralValue(scriptBinding); const bool isLiteral = isLiteralValue(scriptBinding);
if (isLiteral) if (isLiteral)
expr = castToLiteral(scriptCode, scriptBinding); expr = castToLiteral(scriptCode, scriptBinding);
appliedChangesToViewer = true;
ClientProxy::instance()->setBindingForObject(debugId, propertyName, expr, isLiteral); ClientProxy::instance()->setBindingForObject(debugId, propertyName, expr, isLiteral);
} }
virtual void resetBindingForObject(int debugId, const QString &propertyName) virtual void resetBindingForObject(int debugId, const QString &propertyName)
{ {
appliedChangesToViewer = true;
ClientProxy::instance()->resetBindingForObject(debugId, propertyName); ClientProxy::instance()->resetBindingForObject(debugId, propertyName);
} }
virtual void removeObject(int debugId) virtual void removeObject(int debugId)
{ {
appliedChangesToViewer = true;
ClientProxy::instance()->destroyQmlObject(debugId); ClientProxy::instance()->destroyQmlObject(debugId);
} }
virtual void createObject(const QString& qmlText, DebugId ref, virtual void createObject(const QString& qmlText, DebugId ref,
const QStringList& importList, const QString& filename) const QStringList& importList, const QString& filename)
{ {
appliedChangesToViewer = true;
referenceRefreshRequired = true; referenceRefreshRequired = true;
ClientProxy::instance()->createQmlObject(qmlText, ref, importList, filename); ClientProxy::instance()->createQmlObject(qmlText, ref, importList, filename);
} }
void checkUnsyncronizableElementChanges(UiObjectDefinition *parentDefinition)
{
if (unsyncronizableChanges == QmlJSLiveTextPreview::NoUnsyncronizableChanges) {
if (parentDefinition->qualifiedTypeNameId
&& parentDefinition->qualifiedTypeNameId->name)
{
QString elementName = parentDefinition->qualifiedTypeNameId->name->asString();
if (elementName == QLatin1String("PropertyChanges")
// State element can be changed, but not its contents like PropertyChanges.
|| elementName == QLatin1String("StateGroup")
|| elementName == QLatin1String("StateChangeScript")
|| elementName == QLatin1String("ParentChange")
|| elementName == QLatin1String("AnchorChanges")
|| elementName == QLatin1String("Connections")
|| elementName == QLatin1String("Binding")
|| elementName == QLatin1String("ListModel")
|| elementName == QLatin1String("ListElement")
|| elementName == QLatin1String("VisualItemModel")
|| elementName == QLatin1String("VisualDataModel")
|| elementName == QLatin1String("Package")
// XmlListModel properties *can* be edited but XmlRole doesn't refresh the model when changed
|| elementName == QLatin1String("XmlRole"))
{
unsyncronizableElementName = elementName;
unsyncronizableChanges = QmlJSLiveTextPreview::ElementChangeWarning;
}
}
if (unsyncronizableChanges != QmlJSLiveTextPreview::NoUnsyncronizableChanges) {
unsyncronizableChangeLine = parentDefinition->firstSourceLocation().startLine;
unsyncronizableChangeColumn = parentDefinition->firstSourceLocation().startColumn;
}
}
}
public: public:
UpdateObserver() : referenceRefreshRequired(false), hasUnsyncronizableChanges(false) {} UpdateObserver() : appliedChangesToViewer(false), referenceRefreshRequired(false), unsyncronizableChanges(QmlJSLiveTextPreview::NoUnsyncronizableChanges) {}
bool appliedChangesToViewer;
bool referenceRefreshRequired; bool referenceRefreshRequired;
bool hasUnsyncronizableChanges; QString unsyncronizableElementName;
QmlJSLiveTextPreview::UnsyncronizableChangeType unsyncronizableChanges;
unsigned unsyncronizableChangeLine; unsigned unsyncronizableChangeLine;
unsigned unsyncronizableChangeColumn; unsigned unsyncronizableChangeColumn;
@@ -487,12 +537,6 @@ void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
if (m_applyChangesToQmlObserver) { if (m_applyChangesToQmlObserver) {
m_docWithUnappliedChanges.clear(); m_docWithUnappliedChanges.clear();
if (Inspector::showExperimentalWarning()) {
showExperimentalWarning();
experimentalWarningShown = true;
Inspector::setShowExperimentalWarning(false);
}
if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName() if (doc && m_previousDoc && doc->fileName() == m_previousDoc->fileName()
&& doc->qmlProgram() && m_previousDoc->qmlProgram()) && doc->qmlProgram() && m_previousDoc->qmlProgram())
{ {
@@ -502,8 +546,15 @@ void QmlJSLiveTextPreview::documentChanged(QmlJS::Document::Ptr doc)
if (delta.referenceRefreshRequired) if (delta.referenceRefreshRequired)
ClientProxy::instance()->refreshObjectTree(); ClientProxy::instance()->refreshObjectTree();
if (delta.hasUnsyncronizableChanges && !experimentalWarningShown) if (Inspector::showExperimentalWarning() && delta.appliedChangesToViewer) {
showSyncWarning(delta.unsyncronizableChangeLine, delta.unsyncronizableChangeColumn); showExperimentalWarning();
experimentalWarningShown = true;
Inspector::setShowExperimentalWarning(false);
}
if (delta.unsyncronizableChanges != NoUnsyncronizableChanges && !experimentalWarningShown)
showSyncWarning(delta.unsyncronizableChanges, delta.unsyncronizableElementName,
delta.unsyncronizableChangeLine, delta.unsyncronizableChangeColumn);
m_previousDoc = doc; m_previousDoc = doc;
if (!delta.newObjects.isEmpty()) if (!delta.newObjects.isEmpty())
@@ -526,14 +577,29 @@ void QmlJSLiveTextPreview::showExperimentalWarning()
tr("Disable Live Preview"), this, SLOT(disableLivePreview())); tr("Disable Live Preview"), this, SLOT(disableLivePreview()));
} }
void QmlJSLiveTextPreview::showSyncWarning(unsigned line, unsigned column) void QmlJSLiveTextPreview::showSyncWarning(UnsyncronizableChangeType unsyncronizableChangeType,
const QString &elementName, unsigned line, unsigned column)
{ {
Core::EditorManager *em = Core::EditorManager::instance(); Core::EditorManager *em = Core::EditorManager::instance();
em->showEditorInfoBar(Constants::INFO_OUT_OF_SYNC,
tr("The change at line %1, column %2 cannot be applied without reloading the QML application. " QString errorMessage;
"You can continue debugging, but behavior can be unexpected."). switch (unsyncronizableChangeType) {
arg(QString::number(line), QString::number(column)), case AttributeChangeWarning:
tr("Reload"), this, SLOT(reloadQmlViewer())); errorMessage = tr("The %1 attribute at line %2, column %3 cannot be changed without reloading the QML application. ")
.arg(elementName, QString::number(line), QString::number(column));
break;
case ElementChangeWarning:
errorMessage = tr("The %1 element at line %2, column %3 cannot be changed without reloading the QML application. ")
.arg(elementName, QString::number(line), QString::number(column));
break;
case QmlJSLiveTextPreview::NoUnsyncronizableChanges:
default:
return;
}
errorMessage.append(tr("You can continue debugging, but behavior can be unexpected."));
em->showEditorInfoBar(Constants::INFO_OUT_OF_SYNC, errorMessage, tr("Reload"), this, SLOT(reloadQmlViewer()));
} }
void QmlJSLiveTextPreview::reloadQmlViewer() void QmlJSLiveTextPreview::reloadQmlViewer()

View File

@@ -62,7 +62,6 @@ class QmlJSLiveTextPreview : public QObject
public: public:
explicit QmlJSLiveTextPreview(const QmlJS::Document::Ptr &doc, const QmlJS::Document::Ptr &initDoc, QObject *parent = 0); explicit QmlJSLiveTextPreview(const QmlJS::Document::Ptr &doc, const QmlJS::Document::Ptr &initDoc, QObject *parent = 0);
static QmlJS::ModelManagerInterface *modelManager();
//void updateDocuments(); //void updateDocuments();
void associateEditor(Core::IEditor *editor); void associateEditor(Core::IEditor *editor);
@@ -71,6 +70,12 @@ public:
void mapObjectToQml(const QDeclarativeDebugObjectReference &object); void mapObjectToQml(const QDeclarativeDebugObjectReference &object);
void resetInitialDoc(const QmlJS::Document::Ptr &doc); void resetInitialDoc(const QmlJS::Document::Ptr &doc);
enum UnsyncronizableChangeType {
NoUnsyncronizableChanges,
AttributeChangeWarning,
ElementChangeWarning
};
signals: signals:
void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects); void selectedItemsChanged(const QList<QDeclarativeDebugObjectReference> &objects);
void reloadQmlViewerRequested(); void reloadQmlViewerRequested();
@@ -87,9 +92,11 @@ private slots:
void reloadQmlViewer(); void reloadQmlViewer();
private: private:
static QmlJS::ModelManagerInterface *modelManager();
QList<int> objectReferencesForOffset(quint32 offset) const; QList<int> objectReferencesForOffset(quint32 offset) const;
QVariant castToLiteral(const QString &expression, QmlJS::AST::UiScriptBinding *scriptBinding); QVariant castToLiteral(const QString &expression, QmlJS::AST::UiScriptBinding *scriptBinding);
void showSyncWarning(unsigned line, unsigned column); void showSyncWarning(UnsyncronizableChangeType unsyncronizableChangeType, const QString &elementName,
unsigned line, unsigned column);
void showExperimentalWarning(); void showExperimentalWarning();
private: private: