From fc1be8b488aeea55fc022429b7af6ca1997c782c Mon Sep 17 00:00:00 2001 From: Olivier Goffart <olivier.goffart@nokia.com> Date: Thu, 26 Aug 2010 16:02:41 +0200 Subject: [PATCH] Qml Javascript Debugger: add a script console widget. The scriptconsole layout come from the old qmlinspector --- src/plugins/debugger/debuggerconstants.h | 4 +- src/plugins/debugger/debuggerplugin.cpp | 30 ++- src/plugins/debugger/qml/qml.pri | 6 +- src/plugins/debugger/qml/qmlengine.cpp | 20 +- src/plugins/debugger/qml/qmlengine.h | 1 + src/plugins/debugger/qml/scriptconsole.cpp | 256 +++++++++++++++++++++ src/plugins/debugger/qml/scriptconsole.h | 85 +++++++ 7 files changed, 396 insertions(+), 6 deletions(-) create mode 100644 src/plugins/debugger/qml/scriptconsole.cpp create mode 100644 src/plugins/debugger/qml/scriptconsole.h diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index d7c7ebd7289..9a3e379a8ef 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -77,6 +77,7 @@ const char * const DOCKWIDGET_THREADS = "Debugger.Docks.Threads"; const char * const DOCKWIDGET_WATCHERS = "Debugger.Docks.LocalsAndWatchers"; const char * const DOCKWIDGET_QML_INSPECTOR = "Debugger.Docks.QmlInspector"; +const char * const DOCKWIDGET_QML_SCRIPTCONSOLE = "Debugger.Docks.ScriptConsole"; namespace Internal { enum { debug = 0 }; @@ -170,7 +171,8 @@ enum LogChannel AppOutput, AppError, AppStuff, - StatusBar // LogStatus and also put to the status bar + StatusBar, // LogStatus and also put to the status bar + ScriptConsoleOutput }; enum ModelRoles diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 4c2fe2b3f5b..6aa2a77aaba 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -113,6 +113,8 @@ #include <utils/savedaction.h> #include <utils/styledbar.h> +#include <qml/scriptconsole.h> + #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QDir> @@ -925,6 +927,8 @@ public slots: QList<DebuggerRunControl *> runControls() const { return m_snapshotHandler->runControls(); } + void scriptExpressionEntered(const QString&); + public: DebuggerState m_state; uint m_capabilities; @@ -964,6 +968,8 @@ public: QDockWidget *m_stackDock; QDockWidget *m_threadsDock; QDockWidget *m_watchDock; + QDockWidget* m_scriptConsoleDock; + QList<QDockWidget *> m_dockWidgets; DebuggerActions m_actions; @@ -980,6 +986,7 @@ public: QAbstractItemView *m_stackWindow; QAbstractItemView *m_threadsWindow; DebuggerOutputWindow *m_outputWindow; + ScriptConsole *m_scriptConsoleWindow; SessionEngine *m_sessionEngine; @@ -1024,6 +1031,7 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(DebuggerPlugin *plugin) m_stackWindow = 0; m_threadsWindow = 0; m_outputWindow = 0; + m_scriptConsoleWindow = 0; m_sessionEngine = 0; m_debugMode = 0; @@ -1089,6 +1097,9 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_watchersWindow = new WatchWindow(WatchWindow::WatchersType); m_watchersWindow->setObjectName(QLatin1String("CppDebugWatchers")); m_commandWindow = new QTreeView; + m_scriptConsoleWindow = new ScriptConsole; + m_scriptConsoleWindow->setWindowTitle(tr("QML Script Console")); + connect(m_scriptConsoleWindow, SIGNAL(expressionEntered(QString)), this, SLOT(scriptExpressionEntered(QString))); // Session related data m_sessionEngine = new SessionEngine; @@ -1266,9 +1277,13 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_watchDock = m_uiSwitcher->createDockWidget(CppLanguage, localsAndWatchers); m_watchDock->setObjectName(QString(DOCKWIDGET_WATCHERS)); + m_scriptConsoleDock = m_uiSwitcher->createDockWidget(QmlLanguage, m_scriptConsoleWindow); + m_scriptConsoleDock->setObjectName(QString(DOCKWIDGET_QML_SCRIPTCONSOLE)); + m_dockWidgets << m_breakDock << m_modulesDock << m_registerDock << m_outputDock << m_snapshotDock << m_stackDock - << m_sourceFilesDock << m_threadsDock << m_watchDock; + << m_sourceFilesDock << m_threadsDock << m_watchDock + << m_scriptConsoleDock; // Do not fail the whole plugin if something goes wrong here. uint cmdLineEnabledEngines = AllEngineTypes; @@ -1322,6 +1337,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *er m_detachAction->setProperty(Role, RequestExecDetachRole); connect(m_detachAction, SIGNAL(triggered()), SLOT(onAction())); + Core::Command *cmd = 0; Core::ActionContainer *mstart = @@ -2067,6 +2083,7 @@ void DebuggerPluginPrivate::setBusyCursor(bool busy) m_threadsWindow->setCursor(cursor); m_watchersWindow->setCursor(cursor); m_snapshotWindow->setCursor(cursor); + m_scriptConsoleWindow->setCursor(cursor); } void DebuggerPluginPrivate::setSimpleDockWidgetArrangement(const Debugger::DebuggerLanguages &activeLanguages) @@ -2106,6 +2123,7 @@ void DebuggerPluginPrivate::setSimpleDockWidgetArrangement(const Debugger::Debug m_threadsDock->show(); m_snapshotDock->show(); uiSwitcher->qmlInspectorWindow()->show(); + m_scriptConsoleDock->show(); } mw->splitDockWidget(mw->toolBarDockWidget(), m_stackDock, Qt::Vertical); @@ -2119,6 +2137,7 @@ void DebuggerPluginPrivate::setSimpleDockWidgetArrangement(const Debugger::Debug mw->tabifyDockWidget(m_watchDock, m_snapshotDock); if (uiSwitcher->qmlInspectorWindow()) mw->tabifyDockWidget(m_watchDock, uiSwitcher->qmlInspectorWindow()); + mw->tabifyDockWidget(m_watchDock, m_scriptConsoleDock); mw->setTrackingEnabled(true); } @@ -2437,6 +2456,12 @@ void DebuggerPluginPrivate::showStatusMessage(const QString &msg0, int timeout) } } +void DebuggerPluginPrivate::scriptExpressionEntered(const QString& expression) +{ + notifyCurrentEngine(RequestExecuteCommandRole, expression); +} + + /////////////////////////////////////////////////////////////////////// // @@ -2600,6 +2625,9 @@ void DebuggerPlugin::showMessage(const QString &msg, int channel, int timeout) ow->showInput(LogInput, msg); ow->showOutput(LogInput, msg); break; + case ScriptConsoleOutput: + d->m_scriptConsoleWindow->appendResult(msg); + break; default: ow->showOutput(channel, msg); break; diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index 622215080d3..e9621a0c92a 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -5,9 +5,11 @@ HEADERS += \ $$PWD/qmladapter.h \ $$PWD/qmldebuggerclient.h \ $$PWD/qmljsprivateapi.h \ - $$PWD/qmlcppengine.h + $$PWD/qmlcppengine.h \ + $$PWD/scriptconsole.h SOURCES += \ $$PWD/qmlengine.cpp \ $$PWD/qmladapter.cpp \ $$PWD/qmldebuggerclient.cpp \ - $$PWD/qmlcppengine.cpp + $$PWD/qmlcppengine.cpp \ + $$PWD/scriptconsole.cpp diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 6eeb9aa0465..a344ffa7cec 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -593,8 +593,13 @@ void QmlEngine::messageReceived(const QByteArray &message) QByteArray iname; stream >> iname >> data; data.iname = iname; - watchHandler()->insertData(data); - + if (iname.startsWith("watch.")) { + watchHandler()->insertData(data); + } else if(iname == "console") { + plugin()->showMessage(data.value, ScriptConsoleOutput); + } else { + qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value; + } } else if (command == "EXPANDED") { QList<WatchData> result; QByteArray iname; @@ -657,6 +662,17 @@ void QmlEngine::slotMessage(QString err , bool isError) emit runControl()->appendMessage(runControl(), err, isError); } + +void QmlEngine::executeDebuggerCommand(const QString& command) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << QByteArray("EXEC"); + rs << QByteArray("console") << command; + sendMessage(reply); +} + + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 81e98774369..3088af478e7 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -112,6 +112,7 @@ private: bool supportsThreads() const { return false; } void updateWatchData(const WatchData &data); + void executeDebuggerCommand(const QString& command); unsigned int debuggerCapabilities() const; diff --git a/src/plugins/debugger/qml/scriptconsole.cpp b/src/plugins/debugger/qml/scriptconsole.cpp new file mode 100644 index 00000000000..d7726b495be --- /dev/null +++ b/src/plugins/debugger/qml/scriptconsole.cpp @@ -0,0 +1,256 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "scriptconsole.h" + +#include <QtCore/QDebug> +#include <QtGui/QVBoxLayout> +#include <QtGui/QDockWidget> +#include <qmljseditor/qmljshighlighter.h> +#include <utils/styledbar.h> +#include <utils/filterlineedit.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> +#include <debuggeruiswitcher.h> + +namespace Debugger { +namespace Internal { + +ScriptConsole::ScriptConsole(QWidget *parent) + : QWidget(parent), + m_textEdit(new QPlainTextEdit), + m_lineEdit(0) +{ + +// m_prompt = QLatin1String(">"); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(m_textEdit); + m_textEdit->setFrameStyle(QFrame::NoFrame); + + //updateTitle(); + + /*m_highlighter = new QmlJSEditor::Highlighter(m_textEdit->document()); + m_highlighter->setParent(m_textEdit->document());*/ + + Utils::StyledBar *bar = new Utils::StyledBar; + m_lineEdit = new Utils::FilterLineEdit; + + m_lineEdit->setPlaceholderText(tr("<Type expression to evaluate>")); + m_lineEdit->setToolTip(tr("Write and evaluate QtScript expressions.")); + + /*m_clearButton = new QToolButton(); + m_clearButton->setToolTip(tr("Clear Output")); + m_clearButton->setIcon(QIcon(Core::Constants::ICON_CLEAN_PANE)); + connect(m_clearButton, SIGNAL(clicked()), this, SLOT(clearTextEditor()));*/ + //connect(m_lineEdit, SIGNAL(textChanged(QString)), SLOT(changeContextHelpId(QString))); + + connect(m_lineEdit, SIGNAL(returnPressed()), SLOT(executeExpression())); + QHBoxLayout *hbox = new QHBoxLayout(bar); + hbox->setMargin(1); + hbox->setSpacing(1); + hbox->addWidget(m_lineEdit); + //hbox->addWidget(m_clearButton); + layout->addWidget(bar); + + m_textEdit->setReadOnly(true); + m_lineEdit->installEventFilter(this); + + setFontSettings(); +} + + +void ScriptConsole::setFontSettings() +{ + const TextEditor::FontSettings &fs = TextEditor::TextEditorSettings::instance()->fontSettings(); + static QVector<QString> categories; + if (categories.isEmpty()) { + categories << QLatin1String(TextEditor::Constants::C_NUMBER) + << QLatin1String(TextEditor::Constants::C_STRING) + << QLatin1String(TextEditor::Constants::C_TYPE) + << QLatin1String(TextEditor::Constants::C_KEYWORD) + << QLatin1String(TextEditor::Constants::C_LABEL) + << QLatin1String(TextEditor::Constants::C_COMMENT) + << QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE); + } + + const QVector<QTextCharFormat> formats = fs.toTextCharFormats(categories); +/* m_highlighter->setFormats(formats); + m_highlighter->rehighlight();*/ + m_textEdit->setFont(fs.font()); + m_lineEdit->setFont(fs.font()); +} + + +void ScriptConsole::clear() +{ + clearTextEditor(); + + if (m_lineEdit) + m_lineEdit->clear(); +// appendPrompt(); +} + +void ScriptConsole::clearTextEditor() +{ + m_textEdit->clear(); + m_textEdit->appendPlainText(tr("Script Console\n")); +} + + +/*void ExpressionQueryWidget::updateTitle() +{ + if (m_currObject.debugId() < 0) { + m_title = tr("Expression queries"); + } else { + QString desc = QLatin1String("<") + + m_currObject.className() + QLatin1String(": ") + + (m_currObject.name().isEmpty() ? QLatin1String("<unnamed>") : m_currObject.name()) + + QLatin1String(">"); + m_title = tr("Expression queries (using context for %1)" , "Selected object").arg(desc); + } +}*/ +/* +void ExpressionQueryWidget::appendPrompt() +{ + m_textEdit->moveCursor(QTextCursor::End); + + if (m_mode == SeparateEntryMode) { + m_textEdit->insertPlainText("\n"); + } else { + m_textEdit->appendPlainText(m_prompt); + } +} +*/ + + + +bool ScriptConsole::eventFilter(QObject* obj, QEvent* event) +{ + if (obj == m_textEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter) { + executeExpression(); + return true; + } else if (key == Qt::Key_Backspace) { + // ensure m_expr doesn't contain backspace characters + QTextCursor cursor = m_textEdit->textCursor(); + bool atLastLine = !(cursor.block().next().isValid()); + if (!atLastLine) + return true; + if (cursor.positionInBlock() <= m_prompt.count()) + return true; + cursor.deletePreviousChar(); + m_expr = cursor.block().text().mid(m_prompt.count()); + return true; + } else { + m_textEdit->moveCursor(QTextCursor::End); + m_expr += keyEvent->text(); + } + break; + } + case QEvent::FocusIn: + //checkCurrentContext(); + m_textEdit->moveCursor(QTextCursor::End); + break; + default: + break; + } + } else if (obj == m_lineEdit) { + switch (event->type()) { + case QEvent::KeyPress: + { + QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); + int key = keyEvent->key(); + if (key == Qt::Key_Up && m_lineEdit->text() != m_lastExpr) { + m_expr = m_lineEdit->text(); + if (!m_lastExpr.isEmpty()) + m_lineEdit->setText(m_lastExpr); + } else if (key == Qt::Key_Down) { + m_lineEdit->setText(m_expr); + } + break; + } + case QEvent::FocusIn: + // checkCurrentContext(); + break; + default: + break; + } + } + return QWidget::eventFilter(obj, event); +} + +void ScriptConsole::executeExpression() +{ + m_expr = m_lineEdit->text().trimmed(); + m_expr = m_expr.trimmed(); + if (!m_expr.isEmpty()) { + emit expressionEntered(m_expr); + m_lastExpr = m_expr; + if (m_lineEdit) + m_lineEdit->clear(); + } +} + +void ScriptConsole::appendResult(const QString& result) +{ + m_textEdit->moveCursor(QTextCursor::End); + m_textEdit->insertPlainText(m_expr + " : "); + m_textEdit->insertPlainText(result); + m_textEdit->insertPlainText("\n"); + m_expr.clear(); +} + + +ScriptConsole* ScriptConsole::setupDockWidget() +{ + ScriptConsole *console = new ScriptConsole; + console->setWindowTitle(tr("ECMAScript Console")); + + Debugger::DebuggerUISwitcher *uiSwitcher = Debugger::DebuggerUISwitcher::instance(); + QDockWidget* dockWidget = uiSwitcher->createDockWidget(Debugger::QmlLanguage, + console, Qt::BottomDockWidgetArea); + dockWidget->setObjectName(Debugger::Constants::DOCKWIDGET_QML_SCRIPTCONSOLE); + dockWidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); + //m_inspectorDockWidget->setTitleBarWidget(new QWidget(m_inspectorDockWidget)); + + return console; +} + + +} +} diff --git a/src/plugins/debugger/qml/scriptconsole.h b/src/plugins/debugger/qml/scriptconsole.h new file mode 100644 index 00000000000..faef1ebe4db --- /dev/null +++ b/src/plugins/debugger/qml/scriptconsole.h @@ -0,0 +1,85 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSSCRIPTCONSOLE_H +#define QMLJSSCRIPTCONSOLE_H + +#include <QtGui/QWidget> +#include <QtGui/QToolButton> +#include <QtGui/QPlainTextEdit> + +#include <utils/fancylineedit.h> + +namespace QmlJSEditor { +class Highlighter; +} + +namespace Debugger { +namespace Internal { + +class ScriptConsole : public QWidget +{ + Q_OBJECT +public: + ScriptConsole(QWidget *parent = 0); + + static ScriptConsole *setupDockWidget(); + +public slots: + void appendResult(const QString &result); +signals: + void expressionEntered(const QString &expr); + +protected slots: + void clearTextEditor(); + void executeExpression(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + void setFontSettings(); + void clear(); + +// QToolButton *m_clearButton; + QPlainTextEdit *m_textEdit; + Utils::FancyLineEdit *m_lineEdit; + QString m_prompt; + QString m_expr; + QString m_lastExpr; + + QString m_title; + QmlJSEditor::Highlighter *m_highlighter; + +}; + + +} +} //end namespaces + + +#endif -- GitLab