From 759db2b7b6914ee6a58aa5be107275725468fae6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 10 Jan 2017 16:36:55 +0100 Subject: [PATCH] QmlDesigner: Polish error handling for integrated text editor The integrated text editor requires a couple of fixes and features in the error handling. The errors are now handled by the model and not the document management anymore. The text editor does not get disabled if there is an error. Instead we show the error in a status bar. The form editor is blocked if there is a QML an error and we show the error message inside the form editor. Change-Id: I4bfb9b33b09e444ec1de31dd531ce83b32cbcf88 Reviewed-by: Tim Jenssen --- .../components/debugview/debugview.cpp | 19 +++++++ .../components/debugview/debugview.h | 1 + .../components/formeditor/formeditorview.cpp | 24 +++++++- .../components/formeditor/formeditorview.h | 7 +++ .../formeditor/formeditorwidget.cpp | 41 ++++++++++++++ .../components/formeditor/formeditorwidget.h | 9 +++ .../components/texteditor/texteditor.pri | 6 +- .../texteditor/texteditorstatusbar.cpp | 56 +++++++++++++++++++ .../texteditor/texteditorstatusbar.h | 45 +++++++++++++++ .../components/texteditor/texteditorview.cpp | 19 ++++++- .../components/texteditor/texteditorview.h | 3 + .../texteditor/texteditorwidget.cpp | 27 ++++++++- .../components/texteditor/texteditorwidget.h | 6 ++ .../designercore/include/abstractview.h | 23 +++++--- .../qmldesigner/designercore/include/model.h | 4 ++ .../designercore/include/nodeinstanceview.h | 4 -- .../designercore/include/rewriterview.h | 7 ++- .../instances/nodeinstanceserverproxy.cpp | 16 ++---- .../instances/nodeinstanceserverproxy.h | 1 + .../instances/nodeinstanceview.cpp | 10 ++-- .../designercore/model/abstractview.cpp | 20 ++++++- .../qmldesigner/designercore/model/model.cpp | 11 ++++ .../qmldesigner/designercore/model/model_p.h | 2 + .../designercore/model/rewriterview.cpp | 23 +++++++- .../designercore/model/viewmanager.cpp | 21 ++++++- src/plugins/qmldesigner/designmodewidget.cpp | 50 ++++------------- src/plugins/qmldesigner/designmodewidget.h | 5 +- .../qmldesigner/documentwarningwidget.cpp | 7 +-- .../qmldesigner/documentwarningwidget.h | 2 - src/plugins/qmldesigner/qmldesignerplugin.cpp | 28 +++------- 30 files changed, 383 insertions(+), 114 deletions(-) create mode 100644 src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp create mode 100644 src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.h diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 19b49711ab6..ce4b982267e 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -264,6 +264,25 @@ void DebugView::auxiliaryDataChanged(const ModelNode &node, const PropertyName & } } +void DebugView::documentMessagesChanged(const QList &errors, const QList &warnings) +{ + if (isDebugViewEnabled()) { + QTextStream message; + QString string; + message.setString(&string); + + foreach (const RewriterError &error, errors) { + message << error.toString(); + } + + foreach (const RewriterError &warning, warnings) { + message << warning.toString(); + } + + log("::documentMessageChanged:", string); + } +} + void DebugView::rewriterBeginTransaction() { if (isDebugViewEnabled()) diff --git a/src/plugins/qmldesigner/components/debugview/debugview.h b/src/plugins/qmldesigner/components/debugview/debugview.h index 23e6a7db155..c25f655d75a 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.h +++ b/src/plugins/qmldesigner/components/debugview/debugview.h @@ -65,6 +65,7 @@ public: void propertiesRemoved(const QList &propertyList) override; void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; + void documentMessagesChanged(const QList &errors, const QList &warnings) override; void rewriterBeginTransaction() override; void rewriterEndTransaction() override; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 7a242a1a961..e8ffa47a946 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -258,7 +258,7 @@ WidgetInfo FormEditorView::widgetInfo() if (!m_formEditorWidget) createFormEditorWidget(); - return createWidgetInfo(m_formEditorWidget.data(), 0, "FormEditor", WidgetInfo::CentralPane, 0, tr("Form Editor")); + return createWidgetInfo(m_formEditorWidget.data(), 0, "FormEditor", WidgetInfo::CentralPane, 0, tr("Form Editor"), DesignerWidgetFlags::IgnoreErrors); } FormEditorWidget *FormEditorView::formEditorWidget() @@ -284,6 +284,17 @@ void FormEditorView::selectedNodesChanged(const QList &selectedNodeLi m_scene->update(); } +void FormEditorView::documentMessagesChanged(const QList &errors, const QList &warnings) +{ + if (!errors.isEmpty()) + formEditorWidget()->showErrorMessageBox(errors); + else + formEditorWidget()->hideErrorMessageBox(); + + if (!warnings.isEmpty()) + formEditorWidget()->showWarningMessageBox(warnings); +} + void FormEditorView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList &/*nodeList*/, const QList &/*data*/) { if (identifier == QStringLiteral("puppet crashed")) @@ -511,6 +522,17 @@ double FormEditorView::spacing() const return m_formEditorWidget->spacing(); } +void FormEditorView::gotoError(int line, int column) +{ + if (m_gotoErrorCallback) + m_gotoErrorCallback(line, column); +} + +void FormEditorView::setGotoErrorCallback(std::function gotoErrorCallback) +{ + m_gotoErrorCallback = gotoErrorCallback; +} + QList FormEditorView::adjustStatesForModelNodes(const QList &nodeList) const { QList adjustedNodeList; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index 00b4e637a07..d4b55fb3b1d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -26,6 +26,7 @@ #include +#include #include QT_BEGIN_NAMESPACE @@ -74,6 +75,9 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; + + virtual void documentMessagesChanged(const QList &errors, const QList &warnings) override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; // FormEditorView @@ -109,6 +113,8 @@ public: double containerPadding() const; double spacing() const; void deActivateItemCreator(); + void gotoError(int, int); + void setGotoErrorCallback(std::function gotoErrorCallback); protected: void reset(); @@ -135,6 +141,7 @@ private: //variables std::unique_ptr m_dragTool; AbstractFormEditorTool *m_currentTool; int m_transactionCounter; + std::function m_gotoErrorCallback; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index d4053e5ae53..0fb9389262d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -252,6 +252,34 @@ void FormEditorWidget::setFocus() m_graphicsView->setFocus(Qt::OtherFocusReason); } +void FormEditorWidget::showErrorMessageBox(const QList &errors) +{ + errorWidget()->setErrors(errors); + errorWidget()->setVisible(true); + m_graphicsView->setDisabled(true); + m_toolBox->setDisabled(true); +} + +void FormEditorWidget::hideErrorMessageBox() +{ + if (!m_documentErrorWidget.isNull()) + errorWidget()->setVisible(false); + + m_graphicsView->setDisabled(false); + m_toolBox->setDisabled(false); +} + +void FormEditorWidget::showWarningMessageBox(const QList &warnings) +{ + if (!errorWidget()->warningsEnabled()) + return; + + errorWidget()->setWarnings(warnings); + errorWidget()->setVisible(true); + m_graphicsView->setDisabled(true); + m_toolBox->setDisabled(true); +} + ZoomAction *FormEditorWidget::zoomAction() const { return m_zoomAction.data(); @@ -325,6 +353,19 @@ QRectF FormEditorWidget::rootItemRect() const return m_graphicsView->rootItemRect(); } +DocumentWarningWidget *FormEditorWidget::errorWidget() +{ + if (m_documentErrorWidget.isNull()) { + m_documentErrorWidget = new DocumentWarningWidget(this); + connect(m_documentErrorWidget.data(), &DocumentWarningWidget::gotoCodeClicked, [=] + (const QString &, int codeLine, int codeColumn) { + m_formEditorView->gotoError(codeLine, codeColumn); + }); + } + return m_documentErrorWidget; +} + + } diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h index 95c10bcd82b..be6e37b3cec 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h @@ -24,6 +24,8 @@ ****************************************************************************/ #pragma once +#include + #include #include @@ -72,9 +74,15 @@ public: void setFocus(); + void showErrorMessageBox(const QList &errors); + void hideErrorMessageBox(); + + void showWarningMessageBox(const QList &warnings); + protected: void wheelEvent(QWheelEvent *event); QActionGroup *toolActionGroup() const; + DocumentWarningWidget *errorWidget(); private slots: void changeTransformTool(bool checked); @@ -99,6 +107,7 @@ private: QPointer m_rootHeightAction; QPointer m_backgroundAction; QPointer m_resetAction; + QPointer m_documentErrorWidget; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditor.pri b/src/plugins/qmldesigner/components/texteditor/texteditor.pri index ae66f38eef7..d1583631b3d 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditor.pri +++ b/src/plugins/qmldesigner/components/texteditor/texteditor.pri @@ -1,5 +1,7 @@ VPATH += $$PWD SOURCES += texteditorview.cpp \ - texteditorwidget.cpp + texteditorwidget.cpp \ + $$PWD/texteditorstatusbar.cpp HEADERS += texteditorview.h \ - texteditorwidget.h + texteditorwidget.h \ + $$PWD/texteditorstatusbar.h diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp new file mode 100644 index 00000000000..98622ebaedb --- /dev/null +++ b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "texteditorstatusbar.h" + +#include + +#include + +namespace QmlDesigner { + +TextEditorStatusBar::TextEditorStatusBar(QWidget *parent) : QToolBar(parent), m_label(new QLabel(this)) +{ + QWidget *spacer = new QWidget(this); + spacer->setMinimumWidth(50); + addWidget(spacer); + addWidget(m_label); + + /* We have to set another .css, since the central widget has already a style sheet */ + m_label->setStyleSheet(QString("QLabel { color :%1 }").arg(Utils::creatorTheme()->color(Utils::Theme::TextColorError).name())); +} + +void TextEditorStatusBar::clearText() +{ + m_label->clear(); +} + +void TextEditorStatusBar::setText(const QString &text) +{ + m_label->setText(text); +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.h b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.h new file mode 100644 index 00000000000..493c3a9b814 --- /dev/null +++ b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include +#include + +namespace QmlDesigner { + +class TextEditorStatusBar : public QToolBar +{ + Q_OBJECT +public: + explicit TextEditorStatusBar(QWidget *parent = 0); + void clearText(); + void setText(const QString &text); +private: + QLabel *m_label; +}; + +} // namespace QmlDesigner + + diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 2c40d5ea278..20758c46b38 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -51,7 +51,6 @@ TextEditorView::TextEditorView(QObject *parent) : AbstractView(parent) , m_widget(new TextEditorWidget(this)) { - // not completely sure that we need this to just call the right help method -> Internal::TextEditorContext *textEditorContext = new Internal::TextEditorContext(m_widget.get()); Core::ICore::addContextObject(textEditorContext); } @@ -96,7 +95,7 @@ void TextEditorView::nodeReparented(const ModelNode &/*node*/, const NodeAbstrac WidgetInfo TextEditorView::widgetInfo() { - return createWidgetInfo(m_widget.get(), 0, "TextEditor", WidgetInfo::CentralPane, 0, tr("Text Editor")); + return createWidgetInfo(m_widget.get(), 0, "TextEditor", WidgetInfo::CentralPane, 0, tr("Text Editor"), DesignerWidgetFlags::IgnoreErrors); } QString TextEditorView::contextHelpId() const @@ -123,6 +122,16 @@ void TextEditorView::customNotification(const AbstractView * /*view*/, const QSt { } +void TextEditorView::documentMessagesChanged(const QList &errors, const QList &) +{ + if (errors.isEmpty()) { + m_widget->clearStatusBar(); + } else { + const RewriterError error = errors.first(); + m_widget->setStatusText(QString("%1 (Line: %2)").arg(error.description()).arg(error.line())); + } +} + bool TextEditorView::changeToMoveTool() { return true; @@ -181,6 +190,12 @@ void TextEditorView::rewriterEndTransaction() { } +void TextEditorView::gotoCursorPosition(int line, int column) +{ + if (m_widget) + m_widget->gotoCursorPosition(line, column); +} + void TextEditorView::instancePropertyChanged(const QList > &/*propertyList*/) { } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.h b/src/plugins/qmldesigner/components/texteditor/texteditorview.h index 2c18f178c1a..178096481cb 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.h @@ -59,6 +59,7 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; + void documentMessagesChanged(const QList &errors, const QList &warnings) override; // TextEditorView WidgetInfo widgetInfo() override; @@ -87,6 +88,8 @@ public: void deActivateItemCreator(); + void gotoCursorPosition(int line, int column); + private: std::unique_ptr m_widget; }; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index 91c99606a8f..c287993c85d 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -25,7 +25,9 @@ #include "texteditorwidget.h" +#include #include + #include #include @@ -38,10 +40,13 @@ namespace QmlDesigner { TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) : QWidget() , m_textEditorView(textEditorView) + , m_statusBar(new TextEditorStatusBar(this)) { QBoxLayout *layout = new QVBoxLayout(this); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(m_statusBar); m_updateSelectionTimer.setSingleShot(true); m_updateSelectionTimer.setInterval(200); @@ -52,7 +57,9 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) : QWidget() void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) { m_textEditor.reset(textEditor); + layout()->removeWidget(m_statusBar); layout()->addWidget(textEditor->editorWidget()); + layout()->addWidget(m_statusBar); connect(textEditor->editorWidget(), &QPlainTextEdit::cursorPositionChanged, @@ -104,4 +111,22 @@ void TextEditorWidget::jumpTextCursorToSelectedModelNode() } } +void TextEditorWidget::gotoCursorPosition(int line, int column) +{ + if (m_textEditor) { + m_textEditor->editorWidget()->gotoLine(line, column); + m_textEditor->editorWidget()->setFocus(); + } +} + +void TextEditorWidget::setStatusText(const QString &text) +{ + m_statusBar->setText(text); +} + +void TextEditorWidget::clearStatusBar() +{ + m_statusBar->clearText(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h index 26eca933c9e..5922729ceb9 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.h @@ -34,6 +34,7 @@ namespace QmlDesigner { class TextEditorView; +class TextEditorStatusBar; class TextEditorWidget : public QWidget { @@ -51,6 +52,10 @@ public: QString contextHelpId() const; void jumpTextCursorToSelectedModelNode(); + void gotoCursorPosition(int line, int column); + + void setStatusText(const QString &text); + void clearStatusBar(); private: void updateSelectionByCursorPosition(); @@ -58,6 +63,7 @@ private: std::unique_ptr m_textEditor; QPointer m_textEditorView; QTimer m_updateSelectionTimer; + TextEditorStatusBar *m_statusBar; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 435335f4404..98bb27982f0 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,11 @@ class NodeInstanceView; class RewriterView; class QmlModelState; +enum DesignerWidgetFlags { + DisableOnError, + IgnoreErrors +}; + class WidgetInfo { public: @@ -93,18 +99,13 @@ public: CentralPane // not used }; - WidgetInfo() - : widget(0), - toolBarWidgetFactory(0) - { - } - QString uniqueId; QString tabName; - QWidget *widget; + QWidget *widget = nullptr; int placementPriority; PlacementHint placementHint; - ToolBarWidgetFactoryInterface *toolBarWidgetFactory; + ToolBarWidgetFactoryInterface *toolBarWidgetFactory = nullptr; + DesignerWidgetFlags widgetFlags = DesignerWidgetFlags::DisableOnError; }; class QMLDESIGNERCORE_EXPORT AbstractView : public QObject @@ -162,6 +163,8 @@ public: QList allModelNodes() const; + void emitDocumentMessage(const QList &errors, const QList &warnings = QList()); + void emitDocumentMessage(const QString &error); void emitCustomNotification(const QString &identifier); void emitCustomNotification(const QString &identifier, const QList &nodeList); void emitCustomNotification(const QString &identifier, const QList &nodeList, const QList &data); @@ -227,6 +230,8 @@ public: virtual void scriptFunctionsChanged(const ModelNode &node, const QStringList &scriptFunctionList); + virtual void documentMessagesChanged(const QList &errors, const QList &warnings); + void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); NodeInstanceView *nodeInstanceView() const; @@ -256,7 +261,7 @@ protected: const QString &uniqueId = QString(), WidgetInfo::PlacementHint placementHint = WidgetInfo::NoPane, int placementPriority = 0, - const QString &tabName = QString()); + const QString &tabName = QString(), DesignerWidgetFlags widgetFlags = DesignerWidgetFlags::DisableOnError); private: //functions QList toModelNodeList(const QList &nodeList) const; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index dafc4d2c466..8d9998a8a21 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -26,6 +26,9 @@ #pragma once #include + +#include + #include #include @@ -108,6 +111,7 @@ public: TextModifier *textModifier() const; void setTextModifier(TextModifier *textModifier); + void setDocumentMessages(const QList &errors, const QList &warnings); protected: Model(); diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index 3a450884854..86c734083e0 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -133,10 +133,6 @@ public: void sendToken(const QString &token, int number, const QVector &nodeVector); -signals: - void qmlPuppetCrashed(); - void qmlPuppetError(const QString &errorMessage); - protected: void timerEvent(QTimerEvent *event) override; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index d8ea075273f..3d3ea3b72a1 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -34,6 +34,8 @@ #include #include +#include + namespace QmlJS { class Document; class ScopeChain; @@ -154,8 +156,7 @@ public: QList getCppTypes(); -signals: - void errorsChanged(const QList &errors); + void setWidgetStatusCallback(std::function setWidgetStatusCallback); public slots: void qmlTextChanged(); @@ -172,6 +173,7 @@ protected: // functions void applyModificationGroupChanges(); void applyChanges(); void amendQmlText(); + void notifyErrorsAndWarnings(const QList &errors); private: //variables TextModifier *m_textModifier = nullptr; @@ -190,6 +192,7 @@ private: //variables QString m_lastCorrectQmlSource; QTimer m_amendTimer; bool m_instantQmlTextUpdate = false; + std::function m_setWidgetStatusCallback; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index f507fb83619..b92e31f04c5 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -84,14 +84,15 @@ namespace QmlDesigner { -static void showCannotConnectToPuppetWarningAndSwitchToEditMode() +void NodeInstanceServerProxy::showCannotConnectToPuppetWarningAndSwitchToEditMode() { #ifndef QMLDESIGNER_TEST - Core::AsynchronousMessageBox::warning(QCoreApplication::translate("NodeInstanceServerProxy", "Cannot Connect to QML Emulation Layer (QML Puppet)"), - QCoreApplication::translate("NodeInstanceServerProxy", "The executable of the QML emulation layer (QML Puppet) may not be responding. " - "Switching to another kit might help.")); + Core::AsynchronousMessageBox::warning(tr("Cannot Connect to QML Emulation Layer (QML Puppet)"), + tr("The executable of the QML emulation layer (QML Puppet) may not be responding. " + "Switching to another kit might help.")); QmlDesignerPlugin::instance()->switchToTextModeDeferred(); + m_nodeInstanceView->emitDocumentMessage(tr("Cannot Connect to QML Emulation Layer (QML Puppet)")); #endif } @@ -192,12 +193,7 @@ NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceV } } else { - Core::AsynchronousMessageBox::warning(tr("Cannot Start QML Emulation Layer (QML Puppet)"), - tr("The executable of the QML emulation layer (QML Puppet) process cannot be started or does not respond.")); - -#ifndef QMLDESIGNER_TEST - QmlDesignerPlugin::instance()->switchToTextModeDeferred(); -#endif + showCannotConnectToPuppetWarningAndSwitchToEditMode(); } m_localServer->close(); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h index 009c4b27630..f0984f7304b 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h @@ -102,6 +102,7 @@ private slots: void printEditorProcessOutput(); void printPreviewProcessOutput(); void printRenderProcessOutput(); + void showCannotConnectToPuppetWarningAndSwitchToEditMode(); private: QFile m_captureFileForTest; QTimer m_firstTimer; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 595ed1db6c8..e22b745a1e8 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -191,13 +191,11 @@ void NodeInstanceView::handleCrash() if (elaspsedTimeSinceLastCrash > forceRestartTime) restartProcess(); else - emit qmlPuppetCrashed(); + emitDocumentMessage(tr("Qt Quick emulation layer crashed.")); emitCustomNotification(QStringLiteral("puppet crashed")); } - - void NodeInstanceView::restartProcess() { if (m_restartProcessTimerId) @@ -1266,14 +1264,14 @@ void NodeInstanceView::token(const TokenCommand &command) nodeVector.append(modelNodeForInternalId(instanceId)); } - emitInstanceToken(command.tokenName(), command.tokenNumber(), nodeVector); } void NodeInstanceView::debugOutput(const DebugOutputCommand & command) { + RewriterError error(tr("Qt Quick emulation layer crashed.")); if (command.instanceIds().isEmpty()) { - qmlPuppetError(command.text()); // TODO: connect that somewhere to show that to the user + emitDocumentMessage(command.text()); } else { QVector instanceIdsWithChangedErrors; foreach (qint32 instanceId, command.instanceIds()) { @@ -1282,7 +1280,7 @@ void NodeInstanceView::debugOutput(const DebugOutputCommand & command) if (instance.setError(command.text())) instanceIdsWithChangedErrors.append(instanceId); } else { - qmlPuppetError(command.text()); // TODO: connect that somewhere to show that to the user + emitDocumentMessage(command.text()); } } emitInstanceErrorChange(instanceIdsWithChangedErrors); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 0d8c1ba789b..907156c9186 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -122,7 +122,8 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget, const QString &uniqueId, WidgetInfo::PlacementHint placementHint, int placementPriority, - const QString &tabName) + const QString &tabName, + DesignerWidgetFlags widgetFlags) { WidgetInfo widgetInfo; @@ -132,6 +133,7 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget, widgetInfo.placementHint = placementHint; widgetInfo.placementPriority = placementPriority; widgetInfo.tabName = tabName; + widgetInfo.widgetFlags = widgetFlags; return widgetInfo; } @@ -346,6 +348,10 @@ void AbstractView::scriptFunctionsChanged(const ModelNode &/*node*/, const QStri { } +void AbstractView::documentMessagesChanged(const QList &/*errors*/, const QList &/*warnings*/) +{ +} + QList AbstractView::toModelNodeList(const QList &nodeList) const { return QmlDesigner::toModelNodeList(nodeList, const_cast(this)); @@ -566,7 +572,17 @@ QString AbstractView::contextHelpId() const QList AbstractView::allModelNodes() const { - return toModelNodeList(model()->d->allNodes()); + return toModelNodeList(model()->d->allNodes()); +} + +void AbstractView::emitDocumentMessage(const QString &error) +{ + emitDocumentMessage( { RewriterError(error) } ); +} + +void AbstractView::emitDocumentMessage(const QList &errors, const QList &warnings) +{ + model()->d->setDocumentMessages(errors, warnings); } void AbstractView::emitCustomNotification(const QString &identifier) diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 9ca36c0f9ae..9d385e032d4 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -174,6 +174,12 @@ QUrl ModelPrivate::fileUrl() const return m_fileUrl; } +void ModelPrivate::setDocumentMessages(const QList &errors, const QList &warnings) +{ + foreach (const QPointer &view, m_viewList) + view->documentMessagesChanged(errors, warnings); +} + void ModelPrivate::setFileUrl(const QUrl &fileUrl) { QUrl oldPath = m_fileUrl; @@ -1942,6 +1948,11 @@ void Model::setTextModifier(TextModifier *textModifier) d->m_textModifier = textModifier; } +void Model::setDocumentMessages(const QList &errors, const QList &warnings) +{ + d->setDocumentMessages(errors, warnings); +} + /*! \brief Returns the URL against which relative URLs within the model should be resolved. \return The base URL. diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index b5a6e97474a..6acbfbca53b 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -158,6 +158,8 @@ public: void notifyCurrentStateChanged(const ModelNode &node); + void setDocumentMessages(const QList &errors, const QList &warnings); + void notifyRewriterBeginTransaction(); void notifyRewriterEndTransaction(); diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 5cc829c528d..639d5b1b3a5 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -82,6 +82,9 @@ void RewriterView::modelAttached(Model *model) const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) m_lastCorrectQmlSource = qmlSource; + + if (!(m_errors.isEmpty() && m_warnings.isEmpty())) + notifyErrorsAndWarnings(m_errors); } void RewriterView::modelAboutToBeDetached(Model * /*model*/) @@ -425,6 +428,14 @@ void RewriterView::amendQmlText() emitCustomNotification(EndRewriterAmend); } +void RewriterView::notifyErrorsAndWarnings(const QList &errors) +{ + if (m_setWidgetStatusCallback) + m_setWidgetStatusCallback(errors.isEmpty()); + + emitDocumentMessage(errors, m_warnings); +} + Internal::ModelNodePositionStorage *RewriterView::positionStorage() const { return m_positionStorage.data(); @@ -444,24 +455,25 @@ void RewriterView::clearErrorAndWarnings() { m_errors.clear(); m_warnings.clear(); - emit errorsChanged(m_errors); + notifyErrorsAndWarnings(m_errors); } void RewriterView::setWarnings(const QList &warnings) { m_warnings = warnings; + notifyErrorsAndWarnings(m_errors); } void RewriterView::setErrors(const QList &errors) { m_errors = errors; - emit errorsChanged(m_errors); + notifyErrorsAndWarnings(m_errors); } void RewriterView::addError(const RewriterError &error) { m_errors.append(error); - emit errorsChanged(m_errors); + notifyErrorsAndWarnings(m_errors); } void RewriterView::enterErrorState(const QString &errorMessage) @@ -722,6 +734,11 @@ QList RewriterView::getCppTypes() return cppDataList; } +void RewriterView::setWidgetStatusCallback(std::function setWidgetStatusCallback) +{ + m_setWidgetStatusCallback = setWidgetStatusCallback; +} + void RewriterView::qmlTextChanged() { getCppTypes(); diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index c0ce8759a39..f49f2b2eb12 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -47,6 +47,8 @@ #include +#include + namespace QmlDesigner { class ViewManagerData @@ -75,6 +77,12 @@ static CrumbleBar *crumbleBar() { ViewManager::ViewManager() : d(new ViewManagerData) { + d->formEditorView.setGotoErrorCallback([this](int line, int column) { + d->textEditorView.gotoCursorPosition(line, column); + Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget(); + if (designModeWidget && designModeWidget->centralTabWidget()) + designModeWidget->centralTabWidget()->setCurrentIndex(1); + }); } ViewManager::~ViewManager() @@ -99,6 +107,13 @@ void ViewManager::attachNodeInstanceView() void ViewManager::attachRewriterView() { if (currentDesignDocument()->rewriterView()) { + currentDesignDocument()->rewriterView()->setWidgetStatusCallback([this](bool enable) { + if (enable) + enableWidgets(); + else + disableWidgets(); + }); + currentModel()->setRewriterView(currentDesignDocument()->rewriterView()); currentDesignDocument()->rewriterView()->reactivateTextMofifierChangeSignals(); } @@ -263,13 +278,15 @@ QList ViewManager::widgetInfos() void ViewManager::disableWidgets() { foreach (const WidgetInfo &widgetInfo, widgetInfos()) - widgetInfo.widget->setEnabled(false); + if (widgetInfo.widgetFlags == DesignerWidgetFlags::DisableOnError) + widgetInfo.widget->setEnabled(false); } void ViewManager::enableWidgets() { foreach (const WidgetInfo &widgetInfo, widgetInfos()) - widgetInfo.widget->setEnabled(true); + if (widgetInfo.widgetFlags == DesignerWidgetFlags::DisableOnError) + widgetInfo.widget->setEnabled(true); } void ViewManager::pushFileOnCrumbleBar(const Utils::FileName &fileName) diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 39d78ab0223..a60c54c9e2a 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -132,12 +132,6 @@ DesignModeWidget::DesignModeWidget(QWidget *parent) , m_toolBar(new Core::EditorToolBar(this)) , m_crumbleBar(new CrumbleBar(this)) { - connect(viewManager().nodeInstanceView(), &NodeInstanceView::qmlPuppetCrashed, - [=]() { - RewriterError error(tr("Qt Quick emulation layer crashed")); - updateErrorStatus(QList() << error); - } - ); } DesignModeWidget::~DesignModeWidget() @@ -221,8 +215,6 @@ void DesignModeWidget::enableWidgets() qDebug() << Q_FUNC_INFO; hideWarningWidget(); viewManager().enableWidgets(); - m_leftSideBar->setEnabled(true); - m_rightSideBar->setEnabled(true); m_isDisabled = false; } @@ -232,8 +224,6 @@ void DesignModeWidget::disableWidgets() qDebug() << Q_FUNC_INFO; viewManager().disableWidgets(); - m_leftSideBar->setEnabled(false); - m_rightSideBar->setEnabled(false); m_isDisabled = true; } @@ -242,17 +232,18 @@ void DesignModeWidget::showTextEdit() m_centralTabWidget->setCurrentIndex(m_centralTabWidget->currentIndex() == 0 ? 1 : 0); } -void DesignModeWidget::updateErrorStatus(const QList &errors) +void DesignModeWidget::showWarningMessageBox(const QList &warnings) { - if (debug) - qDebug() << Q_FUNC_INFO << errors.count(); + Q_ASSERT(!warnings.isEmpty()); + warningWidget()->setWarnings(warnings); + warningWidget()->setVisible(true); +} - if (m_isDisabled && errors.isEmpty()) { - enableWidgets(); - } else if (!errors.isEmpty()) { - disableWidgets(); - showErrorMessageBox(errors); - } +bool DesignModeWidget::gotoCodeWasClicked() +{ + if (m_warningWidget) + return warningWidget()->gotoCodeWasClicked(); + return false; } static void hideToolButtons(QList &buttons) @@ -576,27 +567,6 @@ void DesignModeWidget::hideWarningWidget() m_warningWidget->setVisible(false); } -void DesignModeWidget::showErrorMessageBox(const QList &errors) -{ - Q_ASSERT(!errors.isEmpty()); - warningWidget()->setErrors(errors); - warningWidget()->setVisible(true); -} - -void DesignModeWidget::showWarningMessageBox(const QList &warnings) -{ - Q_ASSERT(!warnings.isEmpty()); - warningWidget()->setWarnings(warnings); - warningWidget()->setVisible(true); -} - -bool DesignModeWidget::gotoCodeWasClicked() -{ - if (m_warningWidget) - return warningWidget()->gotoCodeWasClicked(); - return false; -} - CrumbleBar *DesignModeWidget::crumbleBar() const { return m_crumbleBar; diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index ff3acb9914e..ec78be11d7e 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -49,13 +49,13 @@ namespace QmlDesigner { class ItemLibraryWidget; class CrumbleBar; +class DocumentWarningWidget; namespace Internal { class DesignMode; class DocumentWidget; class DesignModeWidget; -class DocumentWarningWidget; class DesignModeWidget : public QWidget { @@ -80,7 +80,7 @@ public: void enableWidgets(); void disableWidgets(); void showTextEdit(); - void showErrorMessageBox(const QList &errors); + void showWarningMessageBox(const QList &warnings); bool gotoCodeWasClicked(); @@ -88,7 +88,6 @@ public: QTabWidget* centralTabWidget() const; public slots: - void updateErrorStatus(const QList &errors); void restoreDefaultView(); void toggleSidebars(); void toggleLeftSidebar(); diff --git a/src/plugins/qmldesigner/documentwarningwidget.cpp b/src/plugins/qmldesigner/documentwarningwidget.cpp index 50ea460b131..475ab5c3dd5 100644 --- a/src/plugins/qmldesigner/documentwarningwidget.cpp +++ b/src/plugins/qmldesigner/documentwarningwidget.cpp @@ -46,8 +46,6 @@ static QString errorToString(const RewriterError &error) return QString("Line: %1: %2").arg(error.line()).arg(error.description()); } -namespace Internal { - DocumentWarningWidget::DocumentWarningWidget(QWidget *parent) : Utils::FakeToolTip(parent) , m_headerLabel(new QLabel(this)) @@ -69,7 +67,6 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent) connect(m_navigateLabel, &QLabel::linkActivated, this, [=](const QString &link) { if (link == QLatin1String("goToCode")) { - hide(); emitGotoCodeClicked(m_messages.at(m_currentMessage)); } else if (link == QLatin1String("previous")) { --m_currentMessage; @@ -81,9 +78,10 @@ DocumentWarningWidget::DocumentWarningWidget(QWidget *parent) }); connect(m_continueButton, &QPushButton::clicked, this, [=]() { - hide(); if (m_mode == ErrorMode) emitGotoCodeClicked(m_messages.at(m_currentMessage)); + else + hide(); }); connect(m_ignoreWarningsCheckBox, &QCheckBox::toggled, this, &DocumentWarningWidget::ignoreCheckBoxToggled); @@ -233,5 +231,4 @@ void DocumentWarningWidget::setMessages(const QList &messages) refreshContent(); } -} // namespace Internal } // namespace Designer diff --git a/src/plugins/qmldesigner/documentwarningwidget.h b/src/plugins/qmldesigner/documentwarningwidget.h index 1ab183fc92e..1161d719167 100644 --- a/src/plugins/qmldesigner/documentwarningwidget.h +++ b/src/plugins/qmldesigner/documentwarningwidget.h @@ -36,7 +36,6 @@ QT_END_NAMESPACE namespace QmlDesigner { -namespace Internal { class DocumentWarningWidget : public Utils::FakeToolTip { @@ -79,5 +78,4 @@ private: bool m_gotoCodeWasClicked = false; }; -} // namespace Internal } // namespace Designer diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index a9fdeaad102..c35a0e8eaa2 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -78,8 +78,6 @@ public: DocumentManager documentManager; ShortCutManager shortCutManager; - QMetaObject::Connection rewriterErrorConnection; - Internal::DesignModeWidget *mainWidget; DesignerSettings settings; @@ -354,11 +352,11 @@ void QmlDesignerPlugin::hideDesigner() { if (currentDesignDocument() && currentModel()) { // the message box handle the cursor jump itself - if (mainWidget()->gotoCodeWasClicked() == false) - jumpTextCursorToSelectedModelNode(); } if (d->documentManager.hasCurrentDesignDocument()) { + if (currentModel() && !mainWidget()->gotoCodeWasClicked()) + jumpTextCursorToSelectedModelNode(); deactivateAutoSynchronization(); d->mainWidget->saveSettings(); } @@ -426,6 +424,7 @@ void QmlDesignerPlugin::activateAutoSynchronization() currentDesignDocument()->updateActiveQtVersion(); currentDesignDocument()->updateCurrentProject(); + d->mainWidget->enableWidgets(); currentDesignDocument()->attachRewriterToModel(); resetModelSelection(); @@ -433,22 +432,13 @@ void QmlDesignerPlugin::activateAutoSynchronization() viewManager().attachComponentView(); viewManager().attachViewsExceptRewriterAndComponetView(); - QList errors = currentDesignDocument()->qmlParseErrors(); - if (errors.isEmpty()) { - selectModelNodeUnderTextCursor(); - d->mainWidget->enableWidgets(); - d->mainWidget->setupNavigatorHistory(currentDesignDocument()->textEditor()); - if (showWarningsForFeaturesInDesigner() && currentDesignDocument()->hasQmlParseWarnings()) - d->mainWidget->showWarningMessageBox(currentDesignDocument()->qmlParseWarnings()); - } else { - d->mainWidget->disableWidgets(); - d->mainWidget->showErrorMessageBox(errors); - } + selectModelNodeUnderTextCursor(); + + d->mainWidget->setupNavigatorHistory(currentDesignDocument()->textEditor()); + if (showWarningsForFeaturesInDesigner() && currentDesignDocument()->hasQmlParseWarnings()) + d->mainWidget->showWarningMessageBox(currentDesignDocument()->qmlParseWarnings()); currentDesignDocument()->updateSubcomponentManager(); - - d->rewriterErrorConnection = connect(rewriterView(), &RewriterView::errorsChanged, - d->mainWidget, &Internal::DesignModeWidget::updateErrorStatus); } void QmlDesignerPlugin::deactivateAutoSynchronization() @@ -457,8 +447,6 @@ void QmlDesignerPlugin::deactivateAutoSynchronization() viewManager().detachComponentView(); viewManager().detachRewriterView(); documentManager().currentDesignDocument()->resetToDocumentModel(); - - disconnect(d->rewriterErrorConnection); } void QmlDesignerPlugin::resetModelSelection()