mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-08 06:48:09 +02:00
Add JSConsole
This commit is contained in:
parent
8aa9cea30e
commit
5a5d5b4983
2 changed files with 291 additions and 0 deletions
229
interface/src/ui/JSConsole.cpp
Normal file
229
interface/src/ui/JSConsole.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
//
|
||||
// JSConsole.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 05/12/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ScriptHighlighting.h"
|
||||
|
||||
#include "JSConsole.h"
|
||||
|
||||
const int NO_CURRENT_HISTORY_COMMAND = -1;
|
||||
const int MAX_HISTORY_SIZE = 64;
|
||||
|
||||
const QString ACTIVE_PROMPT_STYLESHEET = "color: rgb(0, 0, 255);";
|
||||
const QString INACTIVE_PROMPT_STYLESHEET = "color: rgba(0, 0, 0, 0.5);";
|
||||
const QString COMMAND_STYLE = "color: rgb(30, 141, 255);";
|
||||
|
||||
const QString RESULT_ERROR_STYLE = "color: rgb(255, 0, 0);";
|
||||
const QString RESULT_SUCCESS_STYLE = "color: rgb(160, 160, 160);";
|
||||
|
||||
const QString GUTTER_ERROR = "<pre style=\"color: red;\">X</pre>";
|
||||
const QString GUTTER_PREVIOUS_COMMAND = "<pre style=\"color: lightblue;\"><</pre>";
|
||||
|
||||
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
|
||||
QDialog(parent, Qt::WindowStaysOnTopHint),
|
||||
_ui(new Ui::Console),
|
||||
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
|
||||
_commandHistory(),
|
||||
_scriptEngine(scriptEngine) {
|
||||
|
||||
_ui->setupUi(this);
|
||||
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
|
||||
_ui->promptTextEdit->installEventFilter(this);
|
||||
|
||||
connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom()));
|
||||
connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput()));
|
||||
|
||||
|
||||
if (_scriptEngine == NULL) {
|
||||
_scriptEngine = Application::getInstance()->loadScript();
|
||||
}
|
||||
|
||||
connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)),
|
||||
this, SLOT(handleEvalutationFinished(QScriptValue, bool)));
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
|
||||
|
||||
setWindowOpacity(0.95);
|
||||
}
|
||||
|
||||
JSConsole::~JSConsole() {
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void JSConsole::executeCommand(const QString& command) {
|
||||
_commandHistory.prepend(command);
|
||||
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
|
||||
_commandHistory.removeLast();
|
||||
}
|
||||
|
||||
_ui->promptTextEdit->setDisabled(true);
|
||||
_ui->promptGutterLabel->setStyleSheet(INACTIVE_PROMPT_STYLESHEET);
|
||||
|
||||
appendMessage(">", "<pre style='" + COMMAND_STYLE + "'>" + command.toHtmlEscaped() + "</pre>");
|
||||
|
||||
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command));
|
||||
|
||||
resetCurrentCommandHistory();
|
||||
}
|
||||
|
||||
void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) {
|
||||
_ui->promptTextEdit->setDisabled(false);
|
||||
_ui->promptGutterLabel->setStyleSheet(ACTIVE_PROMPT_STYLESHEET);
|
||||
|
||||
// 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 = "<pre style='" + resultColor + "'>" + result.toString().toHtmlEscaped() + "</pre>";
|
||||
appendMessage(gutter, resultStr);
|
||||
}
|
||||
|
||||
void JSConsole::handlePrint(const QString& message) {
|
||||
appendMessage("", message);
|
||||
}
|
||||
|
||||
void JSConsole::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
||||
void JSConsole::showEvent(QShowEvent* event) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
||||
bool JSConsole::eventFilter(QObject* sender, QEvent* event) {
|
||||
if (sender == _ui->promptTextEdit) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
int key = keyEvent->key();
|
||||
|
||||
if ((key == Qt::Key_Return || key == Qt::Key_Enter)) {
|
||||
if (keyEvent->modifiers() & Qt::ShiftModifier) {
|
||||
// If the shift key is being used then treat it as a regular return/enter. If this isn't done,
|
||||
// a new QTextBlock isn't created.
|
||||
keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier);
|
||||
} else {
|
||||
QString command = _ui->promptTextEdit->toPlainText().trimmed();
|
||||
|
||||
if (!command.isEmpty()) {
|
||||
QTextCursor cursor = _ui->promptTextEdit->textCursor();
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.removeSelectedText();
|
||||
|
||||
executeCommand(command);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if (key == Qt::Key_Down) {
|
||||
// Go to the next command in history if the cursor is at the last line of the current command.
|
||||
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
|
||||
int blockCount = _ui->promptTextEdit->document()->blockCount();
|
||||
if (blockNumber == blockCount - 1) {
|
||||
setToNextCommandInHistory();
|
||||
return true;
|
||||
}
|
||||
} else if (key == Qt::Key_Up) {
|
||||
// Go to the previous command in history if the cursor is at the first line of the current command.
|
||||
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
|
||||
if (blockNumber == 0) {
|
||||
setToPreviousCommandInHistory();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JSConsole::setToNextCommandInHistory() {
|
||||
if (_currentCommandInHistory >= 0) {
|
||||
_currentCommandInHistory--;
|
||||
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
|
||||
setAndSelectCommand(_rootCommand);
|
||||
} else {
|
||||
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JSConsole::setToPreviousCommandInHistory() {
|
||||
if (_currentCommandInHistory < (_commandHistory.length() - 1)) {
|
||||
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
|
||||
_rootCommand = _ui->promptTextEdit->toPlainText();
|
||||
}
|
||||
_currentCommandInHistory++;
|
||||
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
|
||||
}
|
||||
}
|
||||
|
||||
void JSConsole::resetCurrentCommandHistory() {
|
||||
_currentCommandInHistory = NO_CURRENT_HISTORY_COMMAND;
|
||||
}
|
||||
|
||||
void JSConsole::resizeTextInput() {
|
||||
_ui->promptTextEdit->setMaximumHeight(_ui->promptTextEdit->document()->size().height());
|
||||
_ui->promptTextEdit->updateGeometry();
|
||||
}
|
||||
|
||||
void JSConsole::setAndSelectCommand(const QString& text) {
|
||||
QTextCursor cursor = _ui->promptTextEdit->textCursor();
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.deleteChar();
|
||||
cursor.insertText(text);
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
}
|
||||
|
||||
void JSConsole::scrollToBottom() {
|
||||
QScrollBar* scrollBar = _ui->scrollArea->verticalScrollBar();
|
||||
scrollBar->setValue(scrollBar->maximum());
|
||||
}
|
||||
|
||||
void JSConsole::appendMessage(const QString& gutter, const QString& message) {
|
||||
QWidget* logLine = new QWidget(_ui->logArea);
|
||||
QHBoxLayout* layout = new QHBoxLayout(logLine);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(4);
|
||||
|
||||
QLabel* gutterLabel = new QLabel(logLine);
|
||||
QLabel* messageLabel = new QLabel(logLine);
|
||||
|
||||
gutterLabel->setFixedWidth(16);
|
||||
gutterLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
QFont font("Courier New");
|
||||
font.setStyleHint(QFont::Monospace);
|
||||
gutterLabel->setFont(font);
|
||||
messageLabel->setFont(font);
|
||||
|
||||
gutterLabel->setText(gutter);
|
||||
messageLabel->setText(message);
|
||||
|
||||
layout->addWidget(gutterLabel);
|
||||
layout->addWidget(messageLabel);
|
||||
logLine->setLayout(layout);
|
||||
|
||||
layout->setAlignment(gutterLabel, Qt::AlignTop);
|
||||
|
||||
layout->setStretch(0, 0);
|
||||
layout->setStretch(1, 1);
|
||||
|
||||
_ui->logArea->layout()->addWidget(logLine);
|
||||
|
||||
_ui->logArea->updateGeometry();
|
||||
scrollToBottom();
|
||||
}
|
62
interface/src/ui/JSConsole.h
Normal file
62
interface/src/ui/JSConsole.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// JSConsole.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 05/12/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_JSConsole_h
|
||||
#define hifi_JSConsole_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_console.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
class JSConsole : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL);
|
||||
~JSConsole();
|
||||
|
||||
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);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
protected slots:
|
||||
void scrollToBottom();
|
||||
void resizeTextInput();
|
||||
void handleEvalutationFinished(QScriptValue result, bool isException);
|
||||
void handlePrint(const QString& message);
|
||||
|
||||
private:
|
||||
void appendMessage(const QString& gutter, const QString& message);
|
||||
void setToNextCommandInHistory();
|
||||
void setToPreviousCommandInHistory();
|
||||
void resetCurrentCommandHistory();
|
||||
|
||||
Ui::Console* _ui;
|
||||
int _currentCommandInHistory;
|
||||
QList<QString> _commandHistory;
|
||||
QString _rootCommand;
|
||||
ScriptEngine* _scriptEngine;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_JSConsole_h
|
Loading…
Reference in a new issue