diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg index 1401853fe4..994eb61efe 100644 --- a/interface/resources/icons/start-script.svg +++ b/interface/resources/icons/start-script.svg @@ -14,7 +14,7 @@ height="45" id="svg3827" version="1.1" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.3.1 r9886" sodipodi:docname="start-script.svg"> @@ -439,16 +439,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="76.804753" - inkscape:cy="13.198134" + inkscape:zoom="22.4" + inkscape:cx="44.04179" + inkscape:cy="22.346221" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1920" - inkscape:window-height="1028" + inkscape:window-width="1680" + inkscape:window-height="997" inkscape:window-x="-8" - inkscape:window-y="-8" + inkscape:window-y="21" inkscape:window-maximized="1" /> @@ -536,5 +536,15 @@ height="44.57473" x="84.498352" y="1050.0748" /> + + + diff --git a/interface/src/ScriptHighlighting.cpp b/interface/src/ScriptHighlighting.cpp index 125ab1e967..1d31f2823b 100644 --- a/interface/src/ScriptHighlighting.cpp +++ b/interface/src/ScriptHighlighting.cpp @@ -16,7 +16,7 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) : QSyntaxHighlighter(parent) { _keywordRegex = QRegExp("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b"); - _qoutedTextRegex = QRegExp("\"[^\"]*(\"){0,1}"); + _quotedTextRegex = QRegExp("(\"[^\"]*(\"){0,1}|\'[^\']*(\'){0,1})"); _multiLineCommentBegin = QRegExp("/\\*"); _multiLineCommentEnd = QRegExp("\\*/"); _numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}"); @@ -29,7 +29,7 @@ void ScriptHighlighting::highlightBlock(const QString& text) { this->highlightKeywords(text); this->formatNumbers(text); this->formatTrueFalse(text); - this->formatQoutedText(text); + this->formatQuotedText(text); this->formatComments(text); } @@ -61,14 +61,14 @@ void ScriptHighlighting::formatComments(const QString& text) { int index = _singleLineComment.indexIn(text); while (index >= 0) { int length = _singleLineComment.matchedLength(); - int quoted_index = _qoutedTextRegex.indexIn(text); + int quoted_index = _quotedTextRegex.indexIn(text); bool valid = true; while (quoted_index >= 0 && valid) { - int quoted_length = _qoutedTextRegex.matchedLength(); + int quoted_length = _quotedTextRegex.matchedLength(); if (quoted_index <= index && index <= (quoted_index + quoted_length)) { valid = false; } - quoted_index = _qoutedTextRegex.indexIn(text, quoted_index + quoted_length); + quoted_index = _quotedTextRegex.indexIn(text, quoted_index + quoted_length); } if (valid) { @@ -78,12 +78,12 @@ void ScriptHighlighting::formatComments(const QString& text) { } } -void ScriptHighlighting::formatQoutedText(const QString& text){ - int index = _qoutedTextRegex.indexIn(text); +void ScriptHighlighting::formatQuotedText(const QString& text){ + int index = _quotedTextRegex.indexIn(text); while (index >= 0) { - int length = _qoutedTextRegex.matchedLength(); + int length = _quotedTextRegex.matchedLength(); setFormat(index, length, Qt::red); - index = _qoutedTextRegex.indexIn(text, index + length); + index = _quotedTextRegex.indexIn(text, index + length); } } diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h index 232d594308..2eb40796e3 100644 --- a/interface/src/ScriptHighlighting.h +++ b/interface/src/ScriptHighlighting.h @@ -29,14 +29,14 @@ protected: void highlightBlock(const QString& text); void highlightKeywords(const QString& text); void formatComments(const QString& text); - void formatQoutedText(const QString& text); + void formatQuotedText(const QString& text); void formatNumbers(const QString& text); void formatTrueFalse(const QString& text); private: QRegExp _alphacharRegex; QRegExp _keywordRegex; - QRegExp _qoutedTextRegex; + QRegExp _quotedTextRegex; QRegExp _multiLineCommentBegin; QRegExp _multiLineCommentEnd; QRegExp _numberRegex; diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 6e5b22399d..b50f84d538 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -9,9 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include +#include #include @@ -35,7 +37,8 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _commandHistory(), - _scriptEngine(scriptEngine) { + _ownScriptEngine(scriptEngine == NULL), + _scriptEngine(NULL) { _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); @@ -51,23 +54,42 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom())); connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput())); - - if (_scriptEngine == NULL) { - _scriptEngine = qApp->loadScript(QString(), false); - } - - connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)), - this, SLOT(handleEvalutationFinished(QScriptValue, bool))); - connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + setScriptEngine(scriptEngine); resizeTextInput(); + + connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished())); } JSConsole::~JSConsole() { + disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + if (_ownScriptEngine) { + _scriptEngine->deleteLater(); + } delete _ui; } +void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { + if (_scriptEngine == scriptEngine && scriptEngine != NULL) { + return; + } + if (_scriptEngine != NULL) { + disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + if (_ownScriptEngine) { + _scriptEngine->deleteLater(); + } + } + + // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine + _ownScriptEngine = scriptEngine == NULL; + _scriptEngine = _ownScriptEngine ? qApp->loadScript(QString(), false) : scriptEngine; + + connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); +} + void JSConsole::executeCommand(const QString& command) { _commandHistory.prepend(command); if (_commandHistory.length() > MAX_HISTORY_SIZE) { @@ -78,23 +100,34 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command)); - - resetCurrentCommandHistory(); + QFuture future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command); + _executeWatcher.setFuture(future); } -void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) { +QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { + QScriptValue result; + QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, + Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command)); + return result; +} + +void JSConsole::commandFinished() { + QScriptValue result = _executeWatcher.result(); + _ui->promptTextEdit->setDisabled(false); // Make sure focus is still on this window - some commands are blocking and can take awhile to execute. if (window()->isActiveWindow()) { _ui->promptTextEdit->setFocus(); } - - QString gutter = (isException || result.isError()) ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; - QString resultColor = (isException || result.isError()) ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; - QString resultStr = "" + result.toString().toHtmlEscaped() + ""; + + bool error = (_scriptEngine->hasUncaughtException() || result.isError()); + QString gutter = error ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; + QString resultColor = error ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; + QString resultStr = "" + result.toString().toHtmlEscaped() + ""; appendMessage(gutter, resultStr); + + resetCurrentCommandHistory(); } void JSConsole::handleError(const QString& message) { @@ -233,3 +266,13 @@ void JSConsole::appendMessage(const QString& gutter, const QString& message) { _ui->logArea->updateGeometry(); scrollToBottom(); } + +void JSConsole::clear() { + QLayoutItem* item; + while ((item = _ui->logArea->layout()->takeAt(0)) != NULL) { + delete item->widget(); + delete item; + } + _ui->logArea->updateGeometry(); + scrollToBottom(); +} diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 98afdf7bf8..cb58beab35 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -31,13 +32,12 @@ public: JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL); ~JSConsole(); + void setScriptEngine(ScriptEngine* scriptEngine = NULL); + void clear(); + public slots: void executeCommand(const QString& command); -signals: - void commandExecuting(const QString& command); - void commandFinished(const QString& result); - protected: void setAndSelectCommand(const QString& command); virtual bool eventFilter(QObject* sender, QEvent* event); @@ -47,19 +47,23 @@ protected: protected slots: void scrollToBottom(); void resizeTextInput(); - void handleEvalutationFinished(QScriptValue result, bool isException); void handlePrint(const QString& message); void handleError(const QString& message); + void commandFinished(); private: void appendMessage(const QString& gutter, const QString& message); void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); + QScriptValue executeCommandInWatcher(const QString& command); + QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QList _commandHistory; + // Keeps track if the script engine is created inside the JSConsole + bool _ownScriptEngine; QString _rootCommand; ScriptEngine* _scriptEngine; }; diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index 76327804b6..0ae13f9c24 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -52,10 +52,16 @@ ScriptEditorWidget::ScriptEditorWidget() : // We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak. new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document()); QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus())); + + _console = new JSConsole(this); + _console->setFixedHeight(CONSOLE_HEIGHT); + _scriptEditorWidgetUI->verticalLayout->addWidget(_console); + connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear); } ScriptEditorWidget::~ScriptEditorWidget() { delete _scriptEditorWidgetUI; + delete _console; } void ScriptEditorWidget::onScriptModified() { @@ -68,6 +74,7 @@ void ScriptEditorWidget::onScriptModified() { void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) { _scriptEngine = NULL; + _console->setScriptEngine(NULL); if (_isRestarting) { _isRestarting = false; setRunning(true); @@ -89,8 +96,6 @@ bool ScriptEditorWidget::setRunning(bool run) { if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } @@ -100,15 +105,15 @@ bool ScriptEditorWidget::setRunning(bool run) { // Reload script so that an out of date copy is not retrieved from the cache _scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true); connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } else { connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - qApp->stopScript(_currentScript); + const QString& scriptURLString = QUrl(_currentScript).toString(); + qApp->stopScript(scriptURLString); _scriptEngine = NULL; } + _console->setScriptEngine(_scriptEngine); return true; } @@ -147,8 +152,6 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } @@ -168,16 +171,14 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { static_cast(this->parent()->parent()->parent())->terminateCurrentTab(); } } - const QString& scriptURLString = QUrl(_currentScript).toString(); _scriptEngine = qApp->getScriptEngine(scriptURLString); if (_scriptEngine != NULL) { connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } + _console->setScriptEngine(_scriptEngine); } bool ScriptEditorWidget::save() { @@ -210,19 +211,11 @@ bool ScriptEditorWidget::questionSave() { QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Interface"), tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); - return button == QMessageBox::Save ? save() : (button == QMessageBox::Cancel ? false : true); + return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard); } return true; } -void ScriptEditorWidget::onScriptError(const QString& message) { - _scriptEditorWidgetUI->debugText->appendPlainText("ERROR: " + message); -} - -void ScriptEditorWidget::onScriptPrint(const QString& message) { - _scriptEditorWidgetUI->debugText->appendPlainText("> " + message); -} - void ScriptEditorWidget::onWindowActivated() { if (!_isReloading) { _isReloading = true; @@ -241,10 +234,8 @@ void ScriptEditorWidget::onWindowActivated() { setRunning(false); // Script is restarted once current script instance finishes. } - } } - _isReloading = false; } } diff --git a/interface/src/ui/ScriptEditorWidget.h b/interface/src/ui/ScriptEditorWidget.h index 8dd847ee6d..f53fd7b718 100644 --- a/interface/src/ui/ScriptEditorWidget.h +++ b/interface/src/ui/ScriptEditorWidget.h @@ -14,6 +14,7 @@ #include +#include "JSConsole.h" #include "ScriptEngine.h" namespace Ui { @@ -47,12 +48,11 @@ public slots: void onWindowActivated(); private slots: - void onScriptError(const QString& message); - void onScriptPrint(const QString& message); void onScriptModified(); void onScriptFinished(const QString& scriptName); private: + JSConsole* _console; Ui::ScriptEditorWidget* _scriptEditorWidgetUI; ScriptEngine* _scriptEngine; QString _currentScript; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index b5c8500083..3c6d0f73d5 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -28,7 +28,6 @@ #include #include "Application.h" -#include "JSConsole.h" #include "PathUtils.h" ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : @@ -59,10 +58,6 @@ ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : _ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg"))); _ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg"))); _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg"))); - - QWidget* console = new JSConsole(this); - console->setFixedHeight(CONSOLE_HEIGHT); - this->layout()->addWidget(console); } ScriptEditorWindow::~ScriptEditorWindow() { @@ -77,10 +72,11 @@ void ScriptEditorWindow::setRunningState(bool run) { } void ScriptEditorWindow::updateButtons() { + bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && + static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning(); _ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1); - _ScriptEditorWindowUI->toggleRunButton->setIcon(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && - static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning() ? - QIcon("../resources/icons/stop-script.svg") : QIcon("../resources/icons/start-script.svg")); + _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ? + "icons/stop-script.svg" : "icons/start-script.svg"))))); } void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { diff --git a/interface/ui/scriptEditorWidget.ui b/interface/ui/scriptEditorWidget.ui index 8aeeff363f..e2e538a595 100644 --- a/interface/ui/scriptEditorWidget.ui +++ b/interface/ui/scriptEditorWidget.ui @@ -128,25 +128,6 @@ - - - - - 0 - 0 - - - - font: 15px "Courier"; - - - true - - - - - - @@ -158,22 +139,4 @@ - - - clearButton - clicked() - debugText - clear() - - - 663 - 447 - - - 350 - 501 - - - -