Commands Manual

About

maxon::CommandClassInterface is a base interface for standardized commands. Such a command operates solely on the given input data and can be used in various situations.

CommandClassInterface

A custom command must implement these two methods:

An implementation for such a data object must be based on maxon::CommandDataInterface which in return in based on maxon::DataDictionaryObjectInterface.

Note
All data related to the execution of the command must be stored in the maxon::CommandDataRef object.

Interactive commands (e.g. tools) can also implement maxon::CommandInteractionClassInterface:

// This example implements both a command data and a simple command.
//------------------------------------------------------------------------------
// Simple data
//------------------------------------------------------------------------------
class ExampleDataImpl : public Component<ExampleDataImpl, CommandDataInterface>
{
MAXON_COMPONENT(NORMAL, DataDictionaryObjectClass);
public:
MAXON_METHOD Result<void> SetData(ForwardingDataPtr&& key, Data&& data)
{
return super.SetData(std::move(key), std::move(data));
}
MAXON_METHOD Result<Data> GetData(const ConstDataPtr& key) const
{
return super.GetData(std::move(key));
}
};
// register data
MAXON_COMPONENT_CLASS_REGISTER(ExampleDataImpl, CommandDataClasses, "net.maxonexample.commanddata.exampledata");
//------------------------------------------------------------------------------
// Simple command
//------------------------------------------------------------------------------
class ExampleCommandImpl : public Component<ExampleCommandImpl, CommandClassInterface>
{
public:
MAXON_METHOD Result<COMMANDSTATE> GetState(CommandDataRef& data) const
{
}
MAXON_METHOD Result<COMMANDRESULT> Execute(CommandDataRef& data) const
{
const COMMANDSTATE commandState = self.GetState(data) iferr_return;
if (commandState != COMMANDSTATE::ENABLED)
// get data from context
const maxon::Int32 valueA = data.Get<maxon::Int32>(0) iferr_return;
const maxon::Int32 valueB = data.Get<maxon::Int32>(1) iferr_return;
// perform command
const maxon::Int32 res = valueA + valueB;
// store result in the context
data.Set(2, res) iferr_return;
}
};
// register command
MAXON_COMPONENT_OBJECT_REGISTER(ExampleCommandImpl, CommandClasses, "net.maxonexample.command.addition");

LegacyCommandClassInterface

Some classic data types like BaseContainer cannot be stored in a maxon::DataDictionary. Therefore a custom data class is needed to store specific custom data. Such a data class can be implemented based on maxon::LegacyCommandDataInterface:

maxon::LegacyCommandClassInterface is based on maxon::CommandClassInterface. It can be implemented if custom data should be handled.

// This example shows a simple data structure to store some pointers.
struct MoGraphSetupData
{
maxon::Result<void> CopyFrom(const MoGraphSetupData& other)
{
_doc = other._doc;
_cloner = other._cloner;
_object = other._object;
return maxon::OK;
}
BaseDocument* _doc = nullptr;
BaseObject* _cloner = nullptr;
BaseObject* _object = nullptr;
};
// This example shows the implementation of a simple command that handles "legacy" data.
//------------------------------------------------------------------------------
// MoGraph setup data
//------------------------------------------------------------------------------
class MoGraphSetupDataImpl : public Component<MoGraphSetupDataImpl, LegacyCommandDataInterface>
{
MAXON_COMPONENT(NORMAL, DataDictionaryObjectClass);
public:
MAXON_METHOD Int GetLegacyDataCount() const
{
return 1;
}
MAXON_METHOD Result<Generic*> GetLegacyData(Int index)
{
Generic* const dtaPtr = reinterpret_cast<Generic*>(&_data);
return dtaPtr;
}
MAXON_METHOD Result<void> SetLegacyData(const Generic* data, Int index)
{
if (data == nullptr)
return maxon::NullptrError(MAXON_SOURCE_LOCATION);
const MoGraphSetupData* const moGraphData = reinterpret_cast<const MoGraphSetupData*>(data);
_data.CopyFrom(*moGraphData) iferr_return;
return OK;
}
MAXON_METHOD Result<void> SetData(ForwardingDataPtr&& key, Data&& data)
{
return super.SetData(std::move(key), std::move(data));
}
MAXON_METHOD Result<Data> GetData(const ConstDataPtr& key) const
{
return super.GetData(std::move(key));
}
private:
MoGraphSetupData _data;
};
//------------------------------------------------------------------------------
// register data
//------------------------------------------------------------------------------
MAXON_COMPONENT_CLASS_REGISTER(MoGraphSetupDataImpl, LegacyCommandDataClasses, "net.maxonexample.legacycommanddata.mographsetup");
//------------------------------------------------------------------------------
// MoGraph setup command. Creates a cloner and uses the given object as input.
//------------------------------------------------------------------------------
class MoGraphSetupCommandImpl : public Component<MoGraphSetupCommandImpl, CommandClassInterface>
{
public:
MAXON_METHOD Result<COMMANDSTATE> GetState(CommandDataRef& data) const
{
}
MAXON_METHOD Result<COMMANDRESULT> Execute(CommandDataRef& data) const
{
const COMMANDSTATE commandState = self.GetState(data) iferr_return;
if (commandState != COMMANDSTATE::ENABLED)
// get and check input data
LegacyCommandDataRef legacyData = Cast<LegacyCommandDataRef>(data);
const maxon::Int dataIndex = 0;
MoGraphSetupData& moData = legacyData.GetLegacyData<MoGraphSetupData>(dataIndex) iferr_return;
if (moData._doc == nullptr)
return maxon::NullptrError(MAXON_SOURCE_LOCATION);
// check for object
if (moData._object == nullptr)
{
// if no object is given, use the active object
BaseObject* const activeObject = moData._doc->GetActiveObject();
// if no object is active, there is nothing to do
if (activeObject == nullptr)
moData._object = activeObject;
}
// create cloner
BaseObject* const cloner = BaseObject::Alloc(1018544);
if (cloner == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION);
moData._doc->InsertObject(cloner, nullptr, nullptr);
// place the object under the cloner
moData._object->Remove();
moData._doc->InsertObject(moData._object, cloner, nullptr);
// store result
moData._cloner = cloner;
}
};
//------------------------------------------------------------------------------
// register command
//------------------------------------------------------------------------------
MAXON_COMPONENT_OBJECT_REGISTER(MoGraphSetupCommandImpl, CommandClasses, "net.maxonexample.command.mographsetup");

Data

The data a command operates on is stored in a data object. A default data implementation is stored at maxon::CommandDataClasses::BASE. A custom data class can be implemented based on:

Calling Commands

A given command can be executed by calling the "Invoke" of the data object.

// This example shows the declarations of published objects
// giving access to the data and command implementations
namespace CommandDataClasses
{
// example data
MAXON_DECLARATION(CommandDataClasses::EntryType, EXAMPLEDATA, "net.maxonexample.commanddata.exampledata");
}
namespace CommandClasses
{
// example command
MAXON_DECLARATION(CommandClasses::EntryType, EXAMPLE, "net.maxonexample.command.addition");
// MoGraph setup command. Must be used with MOGRAPHSETUPDATA.
MAXON_DECLARATION(CommandClasses::EntryType, MOGRAPHSETUP, "net.maxonexample.command.mographsetup");
}
namespace LegacyCommandDataClasses
{
// MoGraph setup data for MOGRAPHSETUP command
MAXON_DECLARATION(LegacyCommandDataClasses::EntryType, MOGRAPHSETUPDATA, "net.maxonexample.legacycommanddata.mographsetup");
}
// This example creates an example data and calls the example command.
// create data
maxon::CommandDataRef data = maxon::CommandDataClasses::EXAMPLEDATA().Create() iferr_return;
data.Set(0, maxon::Int32(100)) iferr_return;
data.Set(1, maxon::Int32(200)) iferr_return;
// invoke command
const auto command = maxon::CommandClasses::EXAMPLE();
const maxon::COMMANDRESULT res = data.Invoke(command, false) iferr_return;
return maxon::OK;
// get result
const maxon::Int32 resultValue = data.Get<maxon::Int32>(2) iferr_return;
DiagnosticOutput("Result: @", resultValue);
// This example creates a specific data for the given command,
// calls that command and receives the resulting object.
// prepare mograph data
// no object is set; the active object of the given document should be used
MoGraphSetupData data;
data._doc = doc;
data._object = nullptr;
const maxon::Int dataIndex = 0;
// create data
maxon::LegacyCommandDataRef legacyData = maxon::LegacyCommandDataClasses::MOGRAPHSETUPDATA().Create() iferr_return;
legacyData.SetLegacyData<MoGraphSetupData>(data, dataIndex) iferr_return;
// invoke command
const auto command = maxon::CommandClasses::MOGRAPHSETUP();
const maxon::COMMANDRESULT res = legacyData.Invoke(command, false) iferr_return;
return maxon::OK;
// get result
const MoGraphSetupData result = legacyData.GetLegacyData<MoGraphSetupData>(dataIndex) iferr_return;
result._cloner->SetName("The New Cloner"_s);

Further Reading