Dev:Action System
This page is a work-in-progress documentation of the action system. At the moment there are only some brief notes.
Data Structure
The action system, for the most part, interacts only with document data structure. As such, it is important to understand what this structure is consists of.
Value (ValueBase): The ValueBase class can store values of any of the 13 Types. As such, using Values requires one to check the type of the given object (by calling value.get_type()) and to know the value type before fetching its contents (e.g. value.get(Real())).
ValueNode: A ValueNode is a value that changes with time. Calling value_node(time) retrieves the value at that given time.
ValueDesc: Instances of this class describe a location where a ValueNode may be rather than that ValueNode itself. (For example "the 'amount' parameter of layer x" or "the 5th point in the bline"). That way, the ValueDesc consistently refers to the same location even if the underlying value has been changed, exported, converted, etc.
Actions typically receive ValueDescs to operate on and will only retrieve the actual value at that point if necessary
Properties of an Action
The majority of actions are accessible by right-clicking on certain ducks, parameters, or layers. At that point, Synfig Studio automatically determines which actions can be called based on the data that is currently selected. The first step is for the action to provide a "Parameter Vocabulary" object that describes what types of parameters it needs in order to function.
get_param_vocab()
Typical implementation:
- Get the param vocab from the parent action. In most cases, it will be "ParamVocab ret(Action::CanvasSpecific::get_param_vocab());"
- Create a parameter description for each of the parameters your action needs(see ParamDesc)
- ParamDesc("new_value",Param::TYPE_VALUE)
- Set the parameter options e.g. "ParamDesc(...).set_local_name(_("ValueBase"))"
- The set actions all return the object, so you should chain them: ParamDesc(...).set_local_name(...).set_supports_multiple(...)
- Once the parameters are set, add them to the Parameter Vocabulary e.g. "ret.push_back(ParamDesc("new_value",Param::TYPE_VALUE).set_local_name(_("ValueBase")));"
However, the parameter types alone are not always sufficient to determine whether the action should be called. As such, checking a given parameter set is done by the action:
is_candidate(const ParamList &x)
Typical implementation:
- The first step of any candidate check is to test whether the requirements of the ParamDesc are met. "if (!candidate_check(get_param_vocab(), x)) return false;"
- At this point, you know that all of your parameters are present and can begin testing for specific cases. (For example, the "activepointsetoff" action is only applicable when the activepoint is currently on)
- If there are no tests to perform, simply return true.
Once the parameter list is checked, Synfig Studio will pass the actual parameters to the action. The action must then retrieve their values and store them internally. This internal storage allows for the action to be un-done and re-done multiple times.\
set_param(const synfig::String& name, const Action::Param ¶m)
Typical implementation:
- Retrieve value of the parameter and store it in an internal variable
The action needs to be able to check whether all of its parameters are set. While the parameter list is checked when the action is called as a result of a right-click menu, any actions called from other places (e.g. by other actions) manually set parameters and must check that the proper parameters are present.
is_ready()
Typical implementation:
- Return false if your custom parameters are unset
- Check that the required parameters for CanvasSpecific actions are met "return Action::CanvasSpecific::is_ready();"
Types of actions
There are two major types of actions.
The first are standard undoable actions
- These inherit from Undoable and CanvasSpecific
- It is necessary to implement perform() and undo()
The second are Super-actions which can only call other actions. Since undoing a Super-action is simply undoing all of its sub-actions, these do not require an undo method.
- Inherit from Super
- The prepare() method creates sub-actions and adds them to the stack by calling "add_action(action)"
- perform() and undo() are inherited from Super and should not be overridden.
Sub-actions are added by:
- First create the action of the needed type "Action::Handle action(Action::create("ValueDescSet"));" or "ValueDescConnect::create()" (TODO: Style review here)
- Set it's parameters:
- The canvas, canvas interface, and current time are usually simply passed on to children
- action->set_param("canvas",get_canvas());
- action->set_param("canvas_interface",get_canvas_interface());
- action->set_param("time",time);
- Other parameters will vary from action to action. For ValueDescSet they might be:
- action->set_param("value_desc",reference_value_desc);
- action->set_param("new_value",value);
- The canvas, canvas interface, and current time are usually simply passed on to children