Merge pull request #167 from aws-lumberyard-dev/scripting/scriptevent_spec_6430

Brought over SC's command line fixes and add_node example script
This commit is contained in:
Luis Sempé
2021-04-20 16:01:05 -07:00
committed by GitHub
5 changed files with 274 additions and 52 deletions
@@ -79,15 +79,19 @@ namespace
// Create the nodes in a horizontal list at the top of the canvas.
AZ::Vector2 pos(20.0f, -100.0f);
AZ::Vector2 pos(20.0f, 20.0f);
for (const auto& index : ui->commandList->selectionModel()->selectedIndexes())
{
if (index.column() != CommandListDataModel::ColumnIndex::Command)
if (index.column() != CommandListDataModel::ColumnIndex::CommandIndex)
{
continue;
}
AZ::Uuid type = dataModel->data(index, CommandListDataModel::CustomRole::Types).value<AZ::Uuid>();
if (type.IsNull())
{
continue;
}
[[maybe_unused]] const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(type);
AZ_Assert(classData, "Failed to find ClassData for ID: %s", type.ToString<AZStd::string>().data());
@@ -115,6 +119,8 @@ namespace ScriptCanvasEditor
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataModel::CommandListDataModel([[maybe_unused]] QWidget* parent /*= nullptr*/)
{
ScriptCanvasCommandLineRequestBus::Handler::BusConnect();
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
@@ -138,12 +144,62 @@ namespace ScriptCanvasEditor
if (add)
{
m_nodeTypes.push_back(classData->m_typeId);
Entry entry;
entry.m_type = classData->m_typeId;
m_entries.emplace_back(entry);
}
}
return true;
}
);
);
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::AddCommand, "add_node", "Adds the specified node to the graph",
[serializeContext](const AZStd::vector<AZStd::string>& nodes)
{
AZ::Uuid nodeTypeToAdd = AZ::Uuid::CreateNull();
if (nodes.size() > 0)
{
const AZStd::string& nodeName = *(nodes.begin());
serializeContext->EnumerateDerived<ScriptCanvas::Node>(
[&nodeName, &nodeTypeToAdd](const AZ::SerializeContext::ClassData* classData, [[maybe_unused]] const AZ::Uuid& classUuid) -> bool
{
if (classData && classData->m_editData)
{
if (nodeName.compare(classData->m_name) == 0)
{
nodeTypeToAdd = classData->m_typeId;
}
}
return true;
}
);
if (!nodeTypeToAdd.IsNull())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(scriptCanvasId, &ScriptCanvasEditor::GeneralRequests::GetActiveScriptCanvasId);
AZ::EntityId graphCanvasGraphId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &ScriptCanvasEditor::GeneralRequests::GetActiveGraphCanvasGraphId);
if (scriptCanvasId.IsValid() && graphCanvasGraphId.IsValid())
{
ScriptCanvasEditor::Nodes::StyleConfiguration styleConfiguration;
AZ::Vector2 pos(100.0f, 20.0f);
NodeIdPair nodePair = ScriptCanvasEditor::Nodes::CreateNode(nodeTypeToAdd, scriptCanvasId, styleConfiguration);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodePair.m_graphCanvasId, pos, false);
}
}
}
}
);
}
CommandListDataModel::~CommandListDataModel()
{
ScriptCanvasCommandLineRequestBus::Handler::BusDisconnect();
}
QModelIndex CommandListDataModel::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const
@@ -162,7 +218,7 @@ namespace ScriptCanvasEditor
int CommandListDataModel::rowCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
{
return static_cast<int>(m_nodeTypes.size());
return static_cast<int>(m_entries.size());
}
int CommandListDataModel::columnCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
@@ -190,19 +246,40 @@ namespace ScriptCanvasEditor
}
}
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
AZ::Uuid nodeType = m_entries[index.row()].m_type;
if (nodeType.IsNull())
{
return QVariant(QString(classData->m_name));
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
AZStd::string command = m_entries[index.row()].m_command;
const auto& entry = m_commands.find(command);
if (entry != m_commands.end())
{
return QVariant(QString(entry->second->GetDescription().c_str()));
}
}
}
if (index.column() == ColumnIndex::Description)
else
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::Trail)
{
return QVariant(QString(""));
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType))
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::TrailIndex)
{
return QVariant(QString(""));
}
}
}
}
@@ -210,25 +287,42 @@ namespace ScriptCanvasEditor
{
case CustomRole::Types:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
AZ::Uuid nodeType = m_entries[index.row()].m_type;
return QVariant::fromValue<AZ::Uuid>(nodeType);
}
break;
case CustomRole::Node:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
AZ::Uuid nodeType = m_entries[index.row()].m_type;
if (nodeType.IsNull())
{
return QVariant(QString(classData->m_name));
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
if (index.column() == ColumnIndex::Description)
else
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType))
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::TrailIndex)
{
return QVariant(QString(""));
}
}
}
if (index.column() == ColumnIndex::Trail)
}
break;
case CustomRole::Commands:
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(""));
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
}
break;
@@ -250,21 +344,31 @@ namespace ScriptCanvasEditor
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
for (const auto& entry : m_nodeTypes)
for (const auto& entry : m_entries)
{
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entry);
if (classData)
if (!entry.m_type.IsNull())
{
QString name = QString(classData->m_name);
if (name.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entry.m_type))
{
return true;
QString name = QString(classData->m_name);
if (name.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
{
return true;
}
}
}
else
{
QString commandName = entry.m_command.c_str();
return (commandName.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive));
}
}
return false;
}
ScriptCanvasEditor::Widget::CommandRegistry CommandListDataModel::m_commands;
// CommandLineEdit
/////////////////////////////////////////////////////////////////////////////////////////////
@@ -335,8 +439,25 @@ namespace ScriptCanvasEditor
case Qt::Key_Return:
{
// Invoke the command
// TODO: trigger invoke
// CommandRequestBus::Broadcast(&CommandRequest::Invoke, text().toStdString().c_str());
AZStd::string commandText = text().toStdString().c_str();
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(commandText, tokens, " ");
if (tokens.size() == 1)
{
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::Invoke, tokens.begin()->c_str());
}
else if (tokens.size() > 1)
{
AZStd::string command = *(tokens.begin());
AZStd::vector<AZStd::string> args;
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it)
{
args.push_back(*it);
}
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::InvokeWithArguments, command.c_str(), args);
}
ResetState();
qobject_cast<QWidget*>(parent())->hide();
}
@@ -376,20 +497,29 @@ namespace ScriptCanvasEditor
// CommandListDataProxyModel
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataProxyModel::CommandListDataProxyModel(QObject* parent /*= nullptr*/)
CommandListDataProxyModel::CommandListDataProxyModel(CommandListDataModel* commandListData, QObject* parent /*= nullptr*/)
: QSortFilterProxyModel(parent)
{
QStringList commands;
setSourceModel(commandListData);
QStringList commandList;
CommandListDataModel* commandListData = new CommandListDataModel();
for (int i = 0; i < commandListData->rowCount(); ++i)
{
QModelIndex index = commandListData->index(i, CommandListDataModel::ColumnIndex::Command);
QModelIndex index = commandListData->index(i, CommandListDataModel::ColumnIndex::CommandIndex);
QString command = commandListData->data(index, CommandListDataModel::CustomRole::Node).toString();
commands.push_back(command);
commandList.push_back(command);
}
m_completer = new QCompleter(commands);
ScriptCanvasCommandLineRequests::CommandNameList commands;
ScriptCanvasCommandLineRequestBus::BroadcastResult(commands, &ScriptCanvasCommandLineRequests::GetCommands);
for (auto& command : commands)
{
QString commandName = command.first.c_str();
commandList.push_back(commandName);
}
m_completer = new QCompleter(commandList);
m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
m_completer->setCaseSensitivity(Qt::CaseInsensitive);
}
@@ -421,7 +551,7 @@ namespace ScriptCanvasEditor
}
}
QModelIndex index = dataModel->index(sourceRow, CommandListDataModel::ColumnIndex::Command);
QModelIndex index = dataModel->index(sourceRow, CommandListDataModel::ColumnIndex::CommandIndex);
QString sourceStr = dataModel->data(index).toString();
if (sourceRow > 0 && sourceStr.startsWith(m_input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
@@ -450,8 +580,7 @@ namespace ScriptCanvasEditor
ui->setupUi(this);
CommandListDataModel* commandListDataModel = new CommandListDataModel();
CommandListDataProxyModel* commandListDataProxyModel = new CommandListDataProxyModel();
commandListDataProxyModel->setSourceModel(commandListDataModel);
CommandListDataProxyModel* commandListDataProxyModel = new CommandListDataProxyModel(commandListDataModel);
ui->commandList->setModel(commandListDataProxyModel);
@@ -460,8 +589,8 @@ namespace ScriptCanvasEditor
connect(ui->commandText, &CommandLineEdit::onKeyReleased, this, &CommandLine::onEditKeyReleaseEvent);
connect(ui->commandList, &CommandLineList::onKeyReleased, this, &CommandLine::onListKeyReleaseEvent);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Command, 250);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Description, 1000);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::CommandIndex, 250);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::DescriptionIndex, 1000);
}
void CommandLine::onTextChanged(const QString& text)
@@ -25,6 +25,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Console/Console.h>
#endif
namespace Ui
@@ -36,10 +37,49 @@ namespace ScriptCanvasEditor
{
namespace Widget
{
class Command
{
public:
using Functor = AZStd::function<void(AZStd::vector<AZStd::string>)>;
Command(const AZStd::string& name, const AZStd::string& description, Functor functor)
: m_name(name)
, m_description(description)
, m_functor(functor)
{}
void operator()(const AZStd::vector<AZStd::string>& args)
{
m_functor(args);
}
const AZStd::string& GetName() const { return m_name; }
const AZStd::string& GetDescription() const { return m_description; }
private:
AZStd::string m_name;
AZStd::string m_description;
Functor m_functor;
};
using CommandRegistry = AZStd::unordered_map<AZStd::string, AZStd::unique_ptr<Command>>;
struct ScriptCanvasCommandLineRequests : public AZ::EBusTraits
{
virtual void AddCommand(const AZStd::string commandName, const AZStd::string description, Command::Functor) = 0;
virtual void Invoke(const char* commandName) = 0;
virtual void InvokeWithArguments(const char* commandName, const AZStd::vector<AZStd::string>&) = 0;
using CommandNameList = AZStd::list<AZStd::pair<AZStd::string, AZStd::string>>;
virtual CommandNameList GetCommands() = 0;
};
using ScriptCanvasCommandLineRequestBus = AZ::EBus<ScriptCanvasCommandLineRequests>;
// TODO #lsempe: this deserves its own file
// CommandListDataModel
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandListDataModel : public QAbstractTableModel
, ScriptCanvasCommandLineRequestBus::Handler
{
Q_OBJECT
@@ -49,9 +89,9 @@ namespace ScriptCanvasEditor
enum ColumnIndex
{
Command,
Description,
Trail,
CommandIndex,
DescriptionIndex,
TrailIndex,
Count
};
@@ -65,6 +105,8 @@ namespace ScriptCanvasEditor
};
CommandListDataModel(QWidget* parent = nullptr);
~CommandListDataModel() override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -75,10 +117,62 @@ namespace ScriptCanvasEditor
bool HasMatches(const AZStd::string& input);
struct Entry
{
AZ::Uuid m_type;
AZStd::string m_command;
Entry()
{
m_type = AZ::Uuid::CreateNull();
}
};
protected:
AZStd::vector<AZ::Uuid> m_nodeTypes;
AZStd::vector<Entry> m_entries;
static CommandRegistry m_commands;
void AddCommand(const AZStd::string commandName, const AZStd::string description, Command::Functor f) override
{
if (m_commands.find(commandName) == m_commands.end())
{
m_commands[commandName] = AZStd::make_unique<Command>(commandName, description, f);
Entry entry;
entry.m_command = commandName;
entry.m_type = AZ::Uuid::CreateNull();
m_entries.emplace_back(entry);
}
}
void Invoke(const char* commandName) override
{
auto command = m_commands.find(commandName);
if (command != m_commands.end())
{
command->second->operator()({});
}
}
void InvokeWithArguments(const char* commandName, const AZStd::vector<AZStd::string>& args) override
{
auto command = m_commands.find(commandName);
if (command != m_commands.end())
{
command->second->operator()(args);
}
}
ScriptCanvasCommandLineRequests::CommandNameList GetCommands() override
{
ScriptCanvasCommandLineRequests::CommandNameList commands;
for (auto& command : m_commands)
{
commands.push_back(AZStd::make_pair(command.second->GetName(), command.second->GetDescription()));
}
return commands;
}
};
class CommandListDataProxyModel : public QSortFilterProxyModel
@@ -88,7 +182,7 @@ namespace ScriptCanvasEditor
public:
AZ_CLASS_ALLOCATOR(CommandListDataProxyModel, AZ::SystemAllocator, 0);
CommandListDataProxyModel(QObject* parent = nullptr);
CommandListDataProxyModel(CommandListDataModel* commandListData, QObject* parent = nullptr);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
@@ -168,4 +262,4 @@ namespace ScriptCanvasEditor
AZStd::unique_ptr<Ui::CommandLine> ui;
};
}
}
}
@@ -595,7 +595,6 @@ namespace ScriptCanvasEditor
m_commandLine = new Widget::CommandLine(this);
m_commandLine->setBaseSize(QSize(size().width(), m_commandLine->size().height()));
m_commandLine->setObjectName("CommandLine");
m_commandLine->hide();
m_layout->addWidget(m_commandLine);
m_layout->addWidget(m_emptyCanvas);
@@ -244,7 +244,7 @@
<bool>false</bool>
</property>
<property name="visible">
<bool>false</bool>
<bool>true</bool>
</property>
</action>
<action name="action_ViewNodePalette">
@@ -66,7 +66,7 @@ namespace {{attribute_Namespace}}
{% set deprecationUuid = Class.attrib['DeprecationUUID'] %}
// The following will be injected directly into the source header file for which AzCodeGenerator is being run.
// The following will be injected directly into the source header file for which AZ AutoGen is being run.
// You must #include the generated header into the source header
#define SCRIPTCANVAS_NODE_{{ className }} \
public: \