/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : Dialog for python script terminal #include "StdAfx.h" #include "ScriptTermDialog.h" #include "ScriptHelpDialog.h" #include #include "QtViewPaneManager.h" #include #include #include #include #include #include #include #include CScriptTermDialog::CScriptTermDialog(QWidget* parent) : QWidget(parent) , ui(new Ui::CScriptTermDialog) { ui->setupUi(this); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusConnect(); ui->SCRIPT_INPUT->installEventFilter(this); connect(ui->SCRIPT_INPUT, &QLineEdit::returnPressed, this, &CScriptTermDialog::OnOK); connect(ui->SCRIPT_INPUT, &QLineEdit::textChanged, this, &CScriptTermDialog::OnScriptInputTextChanged); connect(ui->SCRIPT_HELP, &QToolButton::clicked, this, &CScriptTermDialog::OnScriptHelp); connect(ui->SCRIPT_DOCS, &QToolButton::clicked, this, []() { QDesktopServices::openUrl(QUrl("https://docs.aws.amazon.com/lumberyard/latest/tutorials/tutorials-python.html")); }); InitCompleter(); RefreshStyle(); EditorPreferencesNotificationBus::Handler::BusConnect(); } CScriptTermDialog::~CScriptTermDialog() { EditorPreferencesNotificationBus::Handler::BusDisconnect(); AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect(); } void CScriptTermDialog::RegisterViewClass() { AzToolsFramework::ViewPaneOptions options; options.canHaveMultipleInstances = true; options.sendViewPaneNameBackToAmazonAnalyticsServers = true; AzToolsFramework::RegisterViewPane(SCRIPT_TERM_WINDOW_NAME, LyViewPane::CategoryOther, options); } void CScriptTermDialog::RefreshStyle() { // Set the debug/warning text colors appropriately for the background theme // (e.g. not have black text on black background) m_textColor = Qt::black; m_errorColor = QColor(200, 0, 0); // Error (Red) m_warningColor = QColor(128, 112, 0); // Warning (Yellow) if (gSettings.consoleBackgroundColorTheme == SEditorSettings::ConsoleColorTheme::Dark) { m_textColor = Qt::white; m_errorColor = QColor(0xfa, 0x27, 0x27); // Error (Red) m_warningColor = QColor(0xff, 0xaa, 0x22); // Warning (Yellow) } QColor bgColor; if (gSettings.consoleBackgroundColorTheme == SEditorSettings::ConsoleColorTheme::Dark) { bgColor = QColor(0x22, 0x22, 0x22); AzQtComponents::ScrollBar::applyLightStyle(ui->SCRIPT_OUTPUT); } else { bgColor = Qt::white; AzQtComponents::ScrollBar::applyDarkStyle(ui->SCRIPT_OUTPUT); } ui->SCRIPT_OUTPUT->setStyleSheet(QString("QPlainTextEdit{ background: %1 }").arg(bgColor.name(QColor::HexRgb))); // Clear out the console text when we change our background color since // some of the previous text colors may not be appropriate for the // new background color QString text = ui->SCRIPT_OUTPUT->toPlainText(); ui->SCRIPT_OUTPUT->clear(); AppendToConsole(text, m_textColor); } void CScriptTermDialog::InitCompleter() { QStringList inputs; AzToolsFramework::EditorPythonConsoleInterface* editorPythonConsoleInterface = AZ::Interface::Get(); if (editorPythonConsoleInterface) { AzToolsFramework::EditorPythonConsoleInterface::GlobalFunctionCollection globalFunctionCollection; editorPythonConsoleInterface->GetGlobalFunctionList(globalFunctionCollection); for (const AzToolsFramework::EditorPythonConsoleInterface::GlobalFunction& globalFunction : globalFunctionCollection) { inputs.append(QString("%1.%2()").arg(globalFunction.m_moduleName.data()).arg(globalFunction.m_functionName.data())); } } m_lastCommandModel = new QStringListModel(ui->SCRIPT_INPUT); m_completionModel = new QStringListModel(inputs, ui->SCRIPT_INPUT); auto completer = new QCompleter(m_completionModel, ui->SCRIPT_INPUT); ui->SCRIPT_INPUT->setCompleter(completer); } void CScriptTermDialog::OnOK() { const QString command = ui->SCRIPT_INPUT->text(); QString command2 = QLatin1String("] ") % ui->SCRIPT_INPUT->text() % QLatin1String("\r\n"); AppendToConsole(command2, m_textColor); // Add the command to the history. m_lastCommands.removeOne(command); if (!command.isEmpty()) { m_lastCommands.prepend(command); } ExecuteAndPrint(command.toLocal8Bit().data()); //clear script input via a QueuedConnection because completer sets text when it's done, undoing our clear. QMetaObject::invokeMethod(ui->SCRIPT_INPUT, "setText", Qt::QueuedConnection, Q_ARG(QString, "")); } void CScriptTermDialog::OnScriptInputTextChanged(const QString& text) { if (text.isEmpty()) { ui->SCRIPT_INPUT->completer()->setModel(m_completionModel); } } void CScriptTermDialog::OnScriptHelp() { CScriptHelpDialog::GetInstance()->show(); } void CScriptTermDialog::ExecuteAndPrint(const char* cmd) { if (AzToolsFramework::EditorPythonRunnerRequestBus::HasHandlers()) { AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByString, cmd, true); } else { AZ_Warning("python", false, "EditorPythonRunnerRequestBus has no handlers"); } } void CScriptTermDialog::AppendText(const char* pText) { AppendToConsole(QtUtil::ToQString(pText), m_textColor); } void CScriptTermDialog::OnTraceMessage(AZStd::string_view message) { AppendToConsole(QtUtil::ToQString(message.data()), m_textColor); } void CScriptTermDialog::OnErrorMessage(AZStd::string_view message) { AppendToConsole(QtUtil::ToQString(message.data()), m_errorColor, true); } void CScriptTermDialog::OnExceptionMessage(AZStd::string_view message) { AppendToConsole(QtUtil::ToQString(message.data()), m_warningColor, true); } void CScriptTermDialog::OnEditorPreferencesChanged() { RefreshStyle(); } void CScriptTermDialog::AppendToConsole(const QString& string, const QColor& color, bool bold) { QTextCharFormat format; format.setForeground(color); if (bold) { format.setFontWeight(QFont::Bold); } QTextCursor cursor(ui->SCRIPT_OUTPUT->document()); cursor.movePosition(QTextCursor::End); cursor.insertText(string, format); } bool CScriptTermDialog::eventFilter(QObject* obj, QEvent* e) { if (e->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(e); if (keyEvent->key() == Qt::Key_Down) { m_lastCommandModel->setStringList(m_lastCommands); ui->SCRIPT_INPUT->completer()->setModel(m_lastCommandModel); ui->SCRIPT_INPUT->completer()->setCompletionPrefix(""); ui->SCRIPT_INPUT->completer()->complete(); m_upArrowLastCommandIndex = -1; return true; } else if (keyEvent->key() == Qt::Key_Up) { if (!m_lastCommands.isEmpty()) { m_upArrowLastCommandIndex++; if (m_upArrowLastCommandIndex < 0) { m_upArrowLastCommandIndex = 0; } else if (m_upArrowLastCommandIndex >= m_lastCommands.size()) { // Already at the last item, nothing to do return true; } ui->SCRIPT_INPUT->setText(m_lastCommands.at(m_upArrowLastCommandIndex)); } } else { // Reset cycling, we only want for sequential up arrow presses m_upArrowLastCommandIndex = -1; } } return QObject::eventFilter(obj, e); } #include