diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg
new file mode 100644
index 0000000000..86354a555d
--- /dev/null
+++ b/interface/resources/icons/start-script.svg
@@ -0,0 +1,557 @@
+
+
+
+
diff --git a/interface/resources/icons/stop-script.svg b/interface/resources/icons/stop-script.svg
new file mode 100644
index 0000000000..31cdcee749
--- /dev/null
+++ b/interface/resources/icons/stop-script.svg
@@ -0,0 +1,163 @@
+
+
+
+
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 438722df17..769791d09f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -797,6 +797,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (activeWindow() == _window) {
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
+ bool isOption = event->modifiers().testFlag(Qt::AltModifier);
switch (event->key()) {
break;
case Qt::Key_BracketLeft:
@@ -839,9 +840,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_S:
- if (isShifted && isMeta) {
+ if (isShifted && isMeta && !isOption) {
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
- } else if (!isShifted && isMeta) {
+ } else if (isOption && !isShifted && !isMeta) {
+ Menu::getInstance()->triggerOption(MenuOption::ScriptEditor);
+ } else if (!isOption && !isShifted && isMeta) {
takeSnapshot();
} else {
_myAvatar->setDriveKeys(BACK, 1.f);
@@ -3299,13 +3302,14 @@ void Application::stopAllScripts() {
bumpSettings();
}
-void Application::stopScript(const QString &scriptName)
-{
- _scriptEnginesHash.value(scriptName)->stop();
- qDebug() << "stopping script..." << scriptName;
- _scriptEnginesHash.remove(scriptName);
- _runningScriptsWidget->setRunningScripts(getRunningScripts());
- bumpSettings();
+void Application::stopScript(const QString &scriptName) {
+ if (_scriptEnginesHash.contains(scriptName)) {
+ _scriptEnginesHash.value(scriptName)->stop();
+ qDebug() << "stopping script..." << scriptName;
+ _scriptEnginesHash.remove(scriptName);
+ _runningScriptsWidget->setRunningScripts(getRunningScripts());
+ bumpSettings();
+ }
}
void Application::reloadAllScripts() {
@@ -3366,7 +3370,10 @@ void Application::uploadSkeleton() {
uploadFST(false);
}
-void Application::loadScript(const QString& scriptName) {
+ScriptEngine* Application::loadScript(const QString& scriptName, bool focusMainWindow) {
+ if(_scriptEnginesHash.contains(scriptName) && !_scriptEnginesHash[scriptName]->isFinished()){
+ return _scriptEnginesHash[scriptName];
+ }
// start the script on a new thread...
ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), &_controllerScriptingInterface);
@@ -3374,7 +3381,7 @@ void Application::loadScript(const QString& scriptName) {
if (!scriptEngine->hasScript()) {
qDebug() << "Application::loadScript(), script failed to load...";
- return;
+ return NULL;
}
_runningScriptsWidget->setRunningScripts(getRunningScripts());
@@ -3422,8 +3429,12 @@ void Application::loadScript(const QString& scriptName) {
workerThread->start();
// restore the main window's active state
- _window->activateWindow();
+ if (focusMainWindow) {
+ _window->activateWindow();
+ }
bumpSettings();
+
+ return scriptEngine;
}
void Application::loadDialog() {
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 00b71c4ce7..3254c874b6 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -121,7 +121,7 @@ public:
~Application();
void restoreSizeAndPosition();
- void loadScript(const QString& fileNameString);
+ ScriptEngine* loadScript(const QString& fileNameString, bool focusMainWindow = true);
void loadScripts();
void storeSizeAndPosition();
void clearScriptsBeforeRunning();
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 6f70c5616c..4e6dd2eec2 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -195,7 +195,7 @@ Menu::Menu() :
QMenu* toolsMenu = addMenu("Tools");
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
- addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::CTRL | Qt::SHIFT | Qt::Key_S, this, SLOT(showScriptEditor()));
+ addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, this, SLOT(showScriptEditor()));
#ifdef HAVE_QXMPP
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
@@ -1125,7 +1125,7 @@ void Menu::showMetavoxelEditor() {
}
void Menu::showScriptEditor() {
- if(!_ScriptEditor) {
+ if(!_ScriptEditor || !_ScriptEditor->isVisible()) {
_ScriptEditor = new ScriptEditorWindow();
}
_ScriptEditor->raise();
diff --git a/interface/src/ScriptHighlighting.cpp b/interface/src/ScriptHighlighting.cpp
index 119926742c..b2c5ca2ec6 100644
--- a/interface/src/ScriptHighlighting.cpp
+++ b/interface/src/ScriptHighlighting.cpp
@@ -10,6 +10,7 @@
//
#include "ScriptHighlighting.h"
+#include
ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) :
QSyntaxHighlighter(parent)
@@ -19,13 +20,16 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) :
multiLineCommentBegin = QRegExp("/\\*");
multiLineCommentEnd = QRegExp("\\*/");
numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}");
+ singleLineComment = QRegExp("//[^\n]*");
+ truefalseRegex = QRegExp("\\b(true|false)\\b");
}
void ScriptHighlighting::highlightBlock(const QString &text) {
this->highlightKeywords(text);
- this->formatComments(text);
- this->formatQoutedText(text);
this->formatNumbers(text);
+ this->formatTrueFalse(text);
+ this->formatQoutedText(text);
+ this->formatComments(text);
}
void ScriptHighlighting::highlightKeywords(const QString &text) {
@@ -50,6 +54,13 @@ void ScriptHighlighting::formatComments(const QString &text) {
start = text.indexOf(multiLineCommentBegin, start + length);
if (end == -1) setCurrentBlockState(BlockStateInMultiComment);
}
+
+ int index = singleLineComment.indexIn(text);
+ while (index >= 0) {
+ int length = singleLineComment.matchedLength();
+ setFormat(index, length, Qt::lightGray);
+ index = singleLineComment.indexIn(text, index + length);
+ }
}
void ScriptHighlighting::formatQoutedText(const QString &text){
@@ -69,3 +80,14 @@ void ScriptHighlighting::formatNumbers(const QString &text){
index = numberRegex.indexIn(text, index + length);
}
}
+
+void ScriptHighlighting::formatTrueFalse(const QString text){
+ int index = truefalseRegex.indexIn(text);
+ while (index >= 0) {
+ int length = truefalseRegex.matchedLength();
+ QFont* font = new QFont(this->document()->defaultFont());
+ font->setBold(true);
+ setFormat(index, length, *font);
+ index = truefalseRegex.indexIn(text, index + length);
+ }
+}
\ No newline at end of file
diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h
index b9567cb06a..9cbbf277cc 100644
--- a/interface/src/ScriptHighlighting.h
+++ b/interface/src/ScriptHighlighting.h
@@ -26,11 +26,12 @@ public:
};
protected:
- void highlightBlock(const QString &text);
- void highlightKeywords(const QString &text);
- void formatComments(const QString &text);
- void formatQoutedText(const QString &text);
- void formatNumbers(const QString &text);
+ void highlightBlock(const QString& text);
+ void highlightKeywords(const QString& text);
+ void formatComments(const QString& text);
+ void formatQoutedText(const QString& text);
+ void formatNumbers(const QString& text);
+ void formatTrueFalse(const QString text);
private:
QRegExp keywordRegex;
@@ -38,6 +39,8 @@ private:
QRegExp multiLineCommentBegin;
QRegExp multiLineCommentEnd;
QRegExp numberRegex;
+ QRegExp singleLineComment;
+ QRegExp truefalseRegex;
};
#endif // hifi_ScriptHighlighting_h
diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp
index 618e405448..98b6f2fe96 100644
--- a/interface/src/ui/ScriptEditorWidget.cpp
+++ b/interface/src/ui/ScriptEditorWidget.cpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -30,13 +31,126 @@ ScriptEditorWidget::ScriptEditorWidget() :
{
ui->setupUi(this);
+ scriptEngine = NULL;
+
+ connect(ui->scriptEdit->document(), SIGNAL(modificationChanged(bool)), this, SIGNAL(scriptModified()));
+ connect(ui->scriptEdit->document(), SIGNAL(contentsChanged()), this, SLOT(onScriptModified()));
+
// remove the title bar (see the Qt docs on setTitleBarWidget)
setTitleBarWidget(new QWidget());
QFontMetrics fm(this->ui->scriptEdit->font());
this->ui->scriptEdit->setTabStopWidth(fm.width('0') * 4);
ScriptHighlighting* highlighting = new ScriptHighlighting(this->ui->scriptEdit->document());
+ QTimer::singleShot(0, this->ui->scriptEdit, SLOT(setFocus()));
}
ScriptEditorWidget::~ScriptEditorWidget() {
delete ui;
+}
+
+void ScriptEditorWidget::onScriptModified() {
+ if(ui->onTheFlyCheckBox->isChecked() && isRunning()) {
+ setRunning(false);
+ setRunning(true);
+ }
+}
+
+bool ScriptEditorWidget::isModified() {
+ return ui->scriptEdit->document()->isModified();
+}
+
+bool ScriptEditorWidget::isRunning() {
+ return (scriptEngine != NULL) ? scriptEngine->isRunning() : false;
+}
+
+bool ScriptEditorWidget::setRunning(bool run) {
+ if (run && !save()) {
+ return false;
+ }
+ // Clean-up old connections.
+ disconnect(this, SLOT(onScriptError(const QString&)));
+ disconnect(this, SLOT(onScriptPrint(const QString&)));
+
+ if (run) {
+ scriptEngine = Application::getInstance()->loadScript(this->currentScript, false);
+ connect(scriptEngine, SIGNAL(runningStateChanged()), this, SIGNAL(runningStateChanged()));
+
+ // Make new connections.
+ connect(scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(onScriptError(const QString&)));
+ connect(scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(onScriptPrint(const QString&)));
+ } else {
+ Application::getInstance()->stopScript(this->currentScript);
+ scriptEngine = NULL;
+ }
+ return true;
+}
+
+bool ScriptEditorWidget::saveFile(const QString &scriptPath) {
+ QFile file(scriptPath);
+ if (!file.open(QFile::WriteOnly | QFile::Text)) {
+ QMessageBox::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath).arg(file.errorString()));
+ return false;
+ }
+
+ QTextStream out(&file);
+ out << ui->scriptEdit->toPlainText();
+
+ setScriptFile(scriptPath);
+ return true;
+}
+
+void ScriptEditorWidget::loadFile(const QString &scriptPath) {
+ QFile file(scriptPath);
+ if (!file.open(QFile::ReadOnly | QFile::Text)) {
+ QMessageBox::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath).arg(file.errorString()));
+ return;
+ }
+
+ QTextStream in(&file);
+ ui->scriptEdit->setPlainText(in.readAll());
+
+ setScriptFile(scriptPath);
+
+ disconnect(this, SLOT(onScriptError(const QString&)));
+ disconnect(this, SLOT(onScriptPrint(const QString&)));
+
+ scriptEngine = Application::getInstance()->getScriptEngine(scriptPath);
+ if (scriptEngine != NULL) {
+ connect(scriptEngine, SIGNAL(runningStateChanged()), this, SIGNAL(runningStateChanged()));
+ connect(scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(onScriptError(const QString&)));
+ connect(scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(onScriptPrint(const QString&)));
+ }
+}
+
+bool ScriptEditorWidget::save() {
+ return currentScript.isEmpty() ? saveAs() : saveFile(currentScript);
+}
+
+bool ScriptEditorWidget::saveAs() {
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), QString(), tr("Javascript (*.js)"));
+ return !fileName.isEmpty() ? saveFile(fileName) : false;
+}
+
+void ScriptEditorWidget::setScriptFile(const QString& scriptPath) {
+ currentScript = scriptPath;
+ ui->scriptEdit->document()->setModified(false);
+ setWindowModified(false);
+
+ emit scriptnameChanged();
+}
+
+bool ScriptEditorWidget::questionSave() {
+ if (ui->scriptEdit->document()->isModified()) {
+ 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 true;
+}
+
+void ScriptEditorWidget::onScriptError(const QString& message) {
+ ui->debugText->appendPlainText("ERROR: "+ message);
+}
+
+void ScriptEditorWidget::onScriptPrint(const QString& message) {
+ ui->debugText->appendPlainText("> "+message);
}
\ No newline at end of file
diff --git a/interface/src/ui/ScriptEditorWidget.h b/interface/src/ui/ScriptEditorWidget.h
index 931ec105c9..674304acb6 100644
--- a/interface/src/ui/ScriptEditorWidget.h
+++ b/interface/src/ui/ScriptEditorWidget.h
@@ -27,8 +27,30 @@ public:
ScriptEditorWidget();
~ScriptEditorWidget();
+ bool isModified();
+ bool isRunning();
+ bool setRunning(bool run);
+ bool saveFile(const QString& scriptPath);
+ void loadFile(const QString& scriptPath);
+ void setScriptFile(const QString& scriptPath);
+ bool save();
+ bool saveAs();
+ bool questionSave();
+ const QString getScriptName() const { return currentScript; };
+signals:
+ void runningStateChanged();
+ void scriptnameChanged();
+ void scriptModified();
+
+private slots:
+ void onScriptError(const QString& message);
+ void onScriptPrint(const QString& message);
+ void onScriptModified();
+
private:
Ui::ScriptEditorWidget* ui;
+ ScriptEngine* scriptEngine;
+ QString currentScript;
};
#endif // hifi_ScriptEditorWidget_h
diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp
index 38fa26622a..687b992c5f 100644
--- a/interface/src/ui/ScriptEditorWindow.cpp
+++ b/interface/src/ui/ScriptEditorWindow.cpp
@@ -13,8 +13,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -31,32 +33,158 @@ ScriptEditorWindow::ScriptEditorWindow() :
{
ui->setupUi(this);
show();
+ addScriptEditorWidget("New script");
+ loadMenu = new QMenu();
+ connect(loadMenu, SIGNAL(aboutToShow()), this, SLOT(loadMenuAboutToShow()));
+ ui->loadButton->setMenu(loadMenu);
+
+ saveMenu = new QMenu();
+ saveMenu->addAction("Save as..", this, SLOT(saveScriptAsClicked()), Qt::CTRL|Qt::SHIFT|Qt::Key_S);
+
+ ui->saveButton->setMenu(saveMenu);
+
+ connect(new QShortcut(QKeySequence("Ctrl+N"), this), SIGNAL(activated()), this, SLOT(newScriptClicked()));
+ connect(new QShortcut(QKeySequence("Ctrl+S"), this), SIGNAL(activated()), this, SLOT(saveScriptClicked()));
+ connect(new QShortcut(QKeySequence("Ctrl+O"), this), SIGNAL(activated()), this, SLOT(loadScriptClicked()));
+ connect(new QShortcut(QKeySequence("F5"), this), SIGNAL(activated()), this, SLOT(toggleRunScriptClicked()));
}
ScriptEditorWindow::~ScriptEditorWindow() {
delete ui;
}
-void ScriptEditorWindow::loadScriptClicked(){
-
+void ScriptEditorWindow::setRunningState(bool run) {
+ if (ui->tabWidget->currentIndex() != -1) {
+ ((ScriptEditorWidget*)ui->tabWidget->currentWidget())->setRunning(run);
+ }
+ this->updateButtons();
}
-void ScriptEditorWindow::newScriptClicked(){
- addScriptEditorWidget(QString("new Script"));
+void ScriptEditorWindow::updateButtons() {
+ ui->toggleRunButton->setEnabled(ui->tabWidget->currentIndex() != -1);
+ ui->toggleRunButton->setIcon(ui->tabWidget->currentIndex() != -1 && ((ScriptEditorWidget*)ui->tabWidget->currentWidget())->isRunning() ? QIcon("../resources/icons/stop-script.svg"):QIcon("../resources/icons/start-script.svg"));
}
-void ScriptEditorWindow::toggleRunScriptClicked(){
-
+void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
+ addScriptEditorWidget("loading...")->loadFile(scriptName);
+ updateButtons();
}
-void ScriptEditorWindow::saveScriptClicked(){
-
+void ScriptEditorWindow::loadScriptClicked() {
+ QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), QString(), tr("Javascript (*.js)"));
+ if (!scriptName.isEmpty()) {
+ addScriptEditorWidget("loading...")->loadFile(scriptName);
+ updateButtons();
+ }
}
-void ScriptEditorWindow::addScriptEditorWidget(QString title){
- ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget();//ScriptEditorWidget();
+void ScriptEditorWindow::loadMenuAboutToShow() {
+ loadMenu->clear();
+ QStringList runningScripts = Application::getInstance()->getRunningScripts();
+ if (runningScripts.count() > 0) {
+ QSignalMapper* signalMapper = new QSignalMapper(this);
+ foreach (const QString& runningScript, runningScripts) {
+ QAction* runningScriptAction = new QAction(runningScript, loadMenu);
+ connect(runningScriptAction, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ signalMapper->setMapping(runningScriptAction, runningScript);
+ loadMenu->addAction(runningScriptAction);
+ }
+ connect(signalMapper, SIGNAL(mapped(const QString &)), this, SLOT(loadScriptMenu(const QString &)));
+ } else {
+ QAction* naAction = new QAction("(no running scripts)",loadMenu);
+ naAction->setDisabled(true);
+ loadMenu->addAction(naAction);
+ }
+}
+
+void ScriptEditorWindow::newScriptClicked() {
+ addScriptEditorWidget(QString("New script"));
+}
+
+void ScriptEditorWindow::toggleRunScriptClicked() {
+ this->setRunningState(!(ui->tabWidget->currentIndex() !=-1 && ((ScriptEditorWidget*)ui->tabWidget->currentWidget())->isRunning()));
+}
+
+void ScriptEditorWindow::saveScriptClicked() {
+ if (ui->tabWidget->currentIndex() != -1) {
+ ScriptEditorWidget* currentScriptWidget = (ScriptEditorWidget*)ui->tabWidget->currentWidget();
+ currentScriptWidget->save();
+ }
+}
+
+void ScriptEditorWindow::saveScriptAsClicked() {
+ if (ui->tabWidget->currentIndex() != -1) {
+ ScriptEditorWidget* currentScriptWidget = (ScriptEditorWidget*)ui->tabWidget->currentWidget();
+ currentScriptWidget->saveAs();
+ }
+}
+
+ScriptEditorWidget* ScriptEditorWindow::addScriptEditorWidget(QString title) {
+ ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget();
+ connect(newScriptEditorWidget, SIGNAL(scriptnameChanged()), this, SLOT(updateScriptNameOrStatus()));
+ connect(newScriptEditorWidget, SIGNAL(scriptModified()), this, SLOT(updateScriptNameOrStatus()));
+ connect(newScriptEditorWidget, SIGNAL(runningStateChanged()), this, SLOT(updateButtons()));
ui->tabWidget->addTab(newScriptEditorWidget, title);
ui->tabWidget->setCurrentWidget(newScriptEditorWidget);
newScriptEditorWidget->setUpdatesEnabled(true);
newScriptEditorWidget->adjustSize();
+ return newScriptEditorWidget;
+}
+
+void ScriptEditorWindow::tabSwitched(int tabIndex) {
+ this->updateButtons();
+ if (ui->tabWidget->currentIndex() != -1) {
+ ScriptEditorWidget* currentScriptWidget = (ScriptEditorWidget*)ui->tabWidget->currentWidget();
+ QString modifiedStar = (currentScriptWidget->isModified()?"*":"");
+ if (currentScriptWidget->getScriptName().length() > 0) {
+ this->setWindowTitle("Script Editor ["+currentScriptWidget->getScriptName()+modifiedStar+"]");
+ } else {
+ this->setWindowTitle("Script Editor [New script"+modifiedStar+"]");
+ }
+ } else {
+ this->setWindowTitle("Script Editor");
+ }
+}
+
+void ScriptEditorWindow::tabCloseRequested(int tabIndex) {
+ ScriptEditorWidget* closingScriptWidget = (ScriptEditorWidget*)ui->tabWidget->widget(tabIndex);
+ if(closingScriptWidget->questionSave()) {
+ ui->tabWidget->removeTab(tabIndex);
+ }
+}
+
+void ScriptEditorWindow::closeEvent(QCloseEvent *event) {
+ bool unsaved_docs_warning = false;
+ for (int i = 0; i < ui->tabWidget->count(); i++ && !unsaved_docs_warning){
+ if(((ScriptEditorWidget*)ui->tabWidget->widget(i))->isModified()){
+ unsaved_docs_warning = true;
+ }
+ }
+
+ if (!unsaved_docs_warning || QMessageBox::warning(this, tr("Interface"), tr("There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost!"), QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Discard) {
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void ScriptEditorWindow::updateScriptNameOrStatus() {
+ ScriptEditorWidget* source = (ScriptEditorWidget*)QObject::sender();
+ QString modifiedStar = (source->isModified()?"*":"");
+ if (source->getScriptName().length() > 0) {
+ for (int i = 0; i < ui->tabWidget->count(); i++){
+ if (ui->tabWidget->widget(i) == source) {
+ ui->tabWidget->setTabText(i,modifiedStar+QFileInfo(source->getScriptName()).fileName());
+ ui->tabWidget->setTabToolTip(i, source->getScriptName());
+ }
+ }
+ }
+
+ if (ui->tabWidget->currentWidget() == source) {
+ if (source->getScriptName().length() > 0) {
+ this->setWindowTitle("Script Editor ["+source->getScriptName()+modifiedStar+"]");
+ } else {
+ this->setWindowTitle("Script Editor [New script"+modifiedStar+"]");
+ }
+ }
}
\ No newline at end of file
diff --git a/interface/src/ui/ScriptEditorWindow.h b/interface/src/ui/ScriptEditorWindow.h
index 718826cf9d..290a9d6051 100644
--- a/interface/src/ui/ScriptEditorWindow.h
+++ b/interface/src/ui/ScriptEditorWindow.h
@@ -13,6 +13,7 @@
#define hifi_ScriptEditorWindow_h
#include
+#include "ScriptEditorWidget.h"
namespace Ui {
class ScriptEditorWindow;
@@ -25,15 +26,29 @@ public:
ScriptEditorWindow();
~ScriptEditorWindow();
+protected:
+ void closeEvent(QCloseEvent *event);
+
private:
Ui::ScriptEditorWindow* ui;
- void addScriptEditorWidget(QString title);
+ QMenu* loadMenu;
+ QMenu* saveMenu;
+ ScriptEditorWidget* addScriptEditorWidget(QString title);
+ void setRunningState(bool run);
+ void setScriptName(const QString& scriptName);
private slots:
+ void loadScriptMenu(const QString& scriptName);
void loadScriptClicked();
void newScriptClicked();
void toggleRunScriptClicked();
void saveScriptClicked();
+ void saveScriptAsClicked();
+ void loadMenuAboutToShow();
+ void tabSwitched(int tabIndex);
+ void tabCloseRequested(int tabIndex);
+ void updateScriptNameOrStatus();
+ void updateButtons();
};
#endif // hifi_ScriptEditorWindow_h
diff --git a/interface/ui/ScriptEditorWidget.ui b/interface/ui/ScriptEditorWidget.ui
index 5878f26c69..88761c91c5 100644
--- a/interface/ui/ScriptEditorWidget.ui
+++ b/interface/ui/ScriptEditorWidget.ui
@@ -6,8 +6,8 @@
0
0
- 702
- 543
+ 691
+ 549
@@ -18,7 +18,7 @@
- 400
+ 541
238
@@ -91,6 +91,13 @@
+ -
+
+
+ Run on the fly (Careful: Any valid change made to the code will run immediately)
+
+
+
-
diff --git a/interface/ui/ScriptEditorWindow.ui b/interface/ui/ScriptEditorWindow.ui
index a612b2b1c9..9e1b08de3e 100644
--- a/interface/ui/ScriptEditorWindow.ui
+++ b/interface/ui/ScriptEditorWindow.ui
@@ -9,8 +9,8 @@
0
0
- 474
- 638
+ 706
+ 682
@@ -20,7 +20,7 @@
- Script editor []
+ Script Editor
font-family: Helvetica, Arial, sans-serif;
@@ -58,7 +58,7 @@
-
- New Script
+ New Script (Ctrl+N)
New
@@ -66,7 +66,7 @@
../resources/icons/new-script.svg
- ../resources/images/pinned.svg../resources/icons/new-script.svg
+ ../resources/icons/new-script.svg../resources/icons/new-script.svg
@@ -91,7 +91,7 @@
- Load Script
+ Load Script (Ctrl+O)
Load
@@ -119,11 +119,17 @@
-
-
-
- 0
- 2
-
+
+
+ 30
+ 0
+
+
+
+
+ 32
+ 0
+
Qt::NoFocus
@@ -132,7 +138,7 @@
Qt::NoContextMenu
- Save Script
+ Save Script (Ctrl+S)
Save
@@ -150,19 +156,22 @@
316
+
+ QToolButton::MenuButtonPopup
+
-
- Toggle Run Script
+ Toggle Run Script (F5)
Run/Stop
- ../resources/images/plus.svg../resources/images/plus.svg
+ ../resources/icons/start-script.svg../resources/icons/start-script.svg
@@ -286,5 +295,37 @@
+
+ tabWidget
+ currentChanged(int)
+ ScriptEditorWindow
+ tabSwitched(int)
+
+
+ 352
+ 360
+
+
+ 352
+ 340
+
+
+
+
+ tabWidget
+ tabCloseRequested(int)
+ ScriptEditorWindow
+ tabCloseRequested(int)
+
+
+ 352
+ 360
+
+
+ 352
+ 340
+
+
+
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 684c55fbb0..eeb1cebe09 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -43,6 +43,11 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng
return soundScriptValue;
}
+static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
+ qDebug() << "script:print()<<" << context->argument(0).toString();
+ engine->evaluate("Script.print('"+context->argument(0).toString()+"')");
+ return QScriptValue();
+}
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
AbstractControllerScriptingInterface* controllerScriptingInterface) :
@@ -115,6 +120,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
_scriptContents = in.readAll();
} else {
qDebug() << "ERROR Loading file:" << fileName;
+ emit errorMessage("ERROR Loading file:" + fileName);
}
} else {
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
@@ -200,6 +206,9 @@ void ScriptEngine::init() {
qScriptRegisterSequenceMetaType >(&_engine);
qScriptRegisterSequenceMetaType >(&_engine);
+ QScriptValue printConstructorValue = _engine.newFunction(debugPrint);
+ _engine.globalObject().setProperty("print", printConstructorValue);
+
QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor);
QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
_engine.globalObject().setProperty("Sound", soundMetaObject);
@@ -246,6 +255,7 @@ void ScriptEngine::evaluate() {
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString();
+ emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + result.toString());
}
}
@@ -266,11 +276,14 @@ void ScriptEngine::run() {
init();
}
_isRunning = true;
+ emit runningStateChanged();
QScriptValue result = _engine.evaluate(_scriptContents);
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
+
qDebug() << "Uncaught exception at line" << line << ":" << result.toString();
+ emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + result.toString());
}
timeval startTime;
@@ -401,6 +414,7 @@ void ScriptEngine::run() {
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << _engine.uncaughtException().toString();
+ emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + _engine.uncaughtException().toString());
}
}
emit scriptEnding();
@@ -436,10 +450,12 @@ void ScriptEngine::run() {
emit finished(_fileNameString);
_isRunning = false;
+ emit runningStateChanged();
}
void ScriptEngine::stop() {
_isFinished = true;
+ emit runningStateChanged();
}
void ScriptEngine::timerFired() {
@@ -510,6 +526,10 @@ QUrl ScriptEngine::resolveInclude(const QString& include) const {
return url;
}
+void ScriptEngine::print(const QString& message) {
+ emit printedMessage(message);
+}
+
void ScriptEngine::include(const QString& includeFile) {
QUrl url = resolveInclude(includeFile);
QString includeContents;
@@ -523,6 +543,7 @@ void ScriptEngine::include(const QString& includeFile) {
includeContents = in.readAll();
} else {
qDebug() << "ERROR Loading file:" << fileName;
+ emit errorMessage("ERROR Loading file:" + fileName);
}
} else {
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
@@ -538,5 +559,6 @@ void ScriptEngine::include(const QString& includeFile) {
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString();
+ emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString());
}
-}
+}
\ No newline at end of file
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 941c6bbe27..9ea99276d3 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -80,6 +80,9 @@ public:
bool hasScript() const { return !_scriptContents.isEmpty(); }
+ bool isFinished() const { return _isFinished; }
+ bool isRunning() const { return _isRunning; }
+
public slots:
void stop();
@@ -88,12 +91,16 @@ public slots:
void clearInterval(QObject* timer) { stopTimer(reinterpret_cast(timer)); }
void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast(timer)); }
void include(const QString& includeFile);
+ void print(const QString& message);
signals:
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString);
void cleanupMenuItem(const QString& menuItemString);
+ void printedMessage(const QString& message);
+ void errorMessage(const QString& message);
+ void runningStateChanged();
protected:
QString _scriptContents;