Difference between revisions of "Dev:Action System"

From Synfig Studio :: Documentation
Jump to: navigation, search
(Some infor on value/valuenode/valuedesc)
m
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
This page is a work-in-progress documentation of the action system.
+
{{Title|Action System}}
At the moment there are only some brief notes.
+
The action system provides a good entry-point into Synfig because it functions as a stand-alone component that only interacts with Synfig's core data structure. This page is intended to provide a quick overview of the structure of the action system.
  
  
Types of actions
+
==Data Structures==
  
*Inherit from [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1Super.html Super]
+
The action system interacts with the underlying data structure of a Synfig document. An action is most likely to affect the parameters of a given layer or object, which are described by the following classes.
**can only call other actions
+
**implement a prepare() method to create sub-actions
+
  
 +
===Value (ValueBase)===
 +
The ValueBase class can store values of any of the {{l|Dev:Types|13 Value Types}}, such as numbers, points, and colors. Before reading or setting any values, it is important to check the type of value being used (by calling value.get_type()). Getting the contents of the value as a C++ type also requires knowing what type is expected (e.g. value.get(Real())).
  
*Inherit from [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1Undoable.html Undoable] and [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1CanvasSpecific.html CanvasSpecific]
+
===ValueNode===
**implement perform() and undo()
+
A {{l|ValueNode}} is a value can change with time. A "converted" or "animated" parameter is always a ValueNode.
  
 +
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, they can refer to "the 'amount' parameter of layer x" or "the 5th point in the spline (bline)". That way, the ValueDesc continues to function even if the underlying value has been changed, exported, converted, etc.
  
get_param_vocab()
+
Actions typically receive ValueDescs to operate on, only retrieving their values when they are necessary for computations.
  
Each action must be able to supply a "Parameter Vocabulary" object. This object specifies what parameters the action requires and what type they need to be.
+
==Properties of an Action==
  
 +
The majority of actions are accessible by right-clicking on certain handle (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());"
 
* 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(see [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1ParamDesc.html ParamDesc])
+
* Create a parameter description for each of the parameters your action needs(see [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1ParamDesc.html ParamDesc])
 
** ParamDesc("new_value",Param::TYPE_VALUE)
 
** ParamDesc("new_value",Param::TYPE_VALUE)
 
** Set the parameter options e.g. "ParamDesc(...).set_local_name(_("ValueBase"))"
 
** 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(...)
 
** The set actions all return the object, so you should chain them: ParamDesc(...).set_local_name(...).set_supports_multiple(...)
* Once the parameters are set, they should be added to the Parameter Vocabulary e.g. "ret.push_back(ParamDesc("new_value",Param::TYPE_VALUE).set_local_name(_("ValueBase")));"
+
* 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")));"
  
  
is_candidate(const ParamList &x)
+
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:
  
Any time the user selects some object (be it a duck, parameter, layer, etc) and right-clicks, she is offered a context menu containing all applicable actions. Synfig checks for candidates by passing them a list of parameters and asking if they are acceptable.
+
===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;"
 
* 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)
 
* 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.
+
* If there are no tests to perform, 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 &param)
+
===set_param(const synfig::String& name, const Action::Param &param)===
  
When passed a parameter, the action must be able to determine if it is the right type and, if so, set an internal variable that stores the value of the parameter. All action parameters are stored in variables so that they can be accessed again if the action is undone or has been undone and now should be performed again.
+
Typical implementation:
 +
* Retrieve value of the parameter and store it in an internal variable
  
  
is_ready()
+
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.
  
This method should check that all internal variables needed have been properly initialized. This test is performed before any action is called to ensure that it has all of its variables set and can be executed safely.
+
===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();"
  
prepare()
+
===get_param(const synfig::String& name, Action::Param &param)===
  
(Only applies to actions inheriting from Super). The prepare function should create sub-actions that need to be performed and call "add_action" or "add_action_front" on them. There is no need to manually implement perform() and undo(), as these will be done automatically by either performing all of the sub-actions or undoing them.
+
Typical implementation:
 +
* Set the value of the parameter from the action point of vue
  
Actions are added by:
+
The action is used in action like renameValueNode / renameLayer ... all case when a previous value was present.
 +
To use get_param you will need Action::set_value_provided() property during the parameter description in {{l|Dev:Action_System#get_param_vocab()|get_param_vocab()}} implemenation.
  
* First create the action of the needed type "Action::Handle action(Action::create("ValueDescSet"));" or "ValueDescConnect::create()" (TODO: Style review here)
+
==Types of actions==
* Set it's parameters:
+
 
**The canvas, canvas interface, and current time are usually simply passed on to children
+
There are two major types of actions.
 +
 
 +
===Undoable actions===
 +
The first are standard undoable actions
 +
*These inherit from [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1Undoable.html Undoable] and [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1CanvasSpecific.html CanvasSpecific]
 +
*It is necessary to implement perform() and undo()
 +
 
 +
===Super-actions===
 +
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 [http://download.tuxfamily.org/synfig/api/synfig-studio/classsynfigapp_1_1Action_1_1Super.html Super]
 +
*perform() and undo() are inherited from Super and should not be overridden.
 +
*Instead, one should implement the prepare() method, which creates sub-actions
 +
 
 +
To add sub-actions:
 +
 
 +
* First create the action of the needed type e.g. "Action::Handle action(Action::create("ValueDescSet"));" or "ValueDescConnect::create()" (TODO: Style review here)
 +
* Set its parameters:
 +
**The canvas, canvas interface, and current time of the current action are usually passed on to children
 
***action->set_param("canvas",get_canvas());
 
***action->set_param("canvas",get_canvas());
 
***action->set_param("canvas_interface",get_canvas_interface());
 
***action->set_param("canvas_interface",get_canvas_interface());
Line 61: Line 96:
 
***action->set_param("value_desc",reference_value_desc);
 
***action->set_param("value_desc",reference_value_desc);
 
***action->set_param("new_value",value);
 
***action->set_param("new_value",value);
 
+
* Add it to the action stack
Terminology:
+
** "add_action(action)"
 
+
Value (ValueBase): The ValueBase class can store values of any of the 13 {{l|Dev:Types|Types}}. Actions involved in setting a value typically require an object of this type for a parameter. Since a given instance of ValueBase may in fact store objects of different types, it is important to check the type (by calling value.get_type()) and then get the value in the proper type (e.g. value.get(Real()))
+
 
+
ValueNode: A {{l|ValueNode}} is a value that changes with 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.
+

Latest revision as of 18:55, 16 March 2017

The action system provides a good entry-point into Synfig because it functions as a stand-alone component that only interacts with Synfig's core data structure. This page is intended to provide a quick overview of the structure of the action system.


Data Structures

The action system interacts with the underlying data structure of a Synfig document. An action is most likely to affect the parameters of a given layer or object, which are described by the following classes.

Value (ValueBase)

The ValueBase class can store values of any of the 13 Value Types, such as numbers, points, and colors. Before reading or setting any values, it is important to check the type of value being used (by calling value.get_type()). Getting the contents of the value as a C++ type also requires knowing what type is expected (e.g. value.get(Real())).

ValueNode

A ValueNode is a value can change with time. A "converted" or "animated" parameter is always a ValueNode.

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, they can refer to "the 'amount' parameter of layer x" or "the 5th point in the spline (bline)". That way, the ValueDesc continues to function even if the underlying value has been changed, exported, converted, etc.

Actions typically receive ValueDescs to operate on, only retrieving their values when they are necessary for computations.

Properties of an Action

The majority of actions are accessible by right-clicking on certain handle (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, 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 &param)

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();"

get_param(const synfig::String& name, Action::Param &param)

Typical implementation:

  • Set the value of the parameter from the action point of vue

The action is used in action like renameValueNode / renameLayer ... all case when a previous value was present. To use get_param you will need Action::set_value_provided() property during the parameter description in get_param_vocab() implemenation.

Types of actions

There are two major types of actions.

Undoable actions

The first are standard undoable actions

Super-actions

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
  • perform() and undo() are inherited from Super and should not be overridden.
  • Instead, one should implement the prepare() method, which creates sub-actions

To add sub-actions:

  • First create the action of the needed type e.g. "Action::Handle action(Action::create("ValueDescSet"));" or "ValueDescConnect::create()" (TODO: Style review here)
  • Set its parameters:
    • The canvas, canvas interface, and current time of the current action are usually 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);
  • Add it to the action stack
    • "add_action(action)"