Table of Contents
We plan for all user actions to be mediated through action objects that can be described by commands; saved in an undo history; and triggered by buttons, menu items, and keyboard shortcuts. A rough draft of an action system can be found on the Vectornaut:action-tinkering branch. Here are some design considerations.
Organizing execution code
Action trait
In the current draft, an action is described by an implementation of the Action trait. This trait defines a single method, execute, which takes an application state reference as input. This organization helps streamline the construction of actions, since the execution code doesn't need to be written in the place where the action is constructed.
Stored closures
Alternatively, an action could be represented by a structure that stores the execution code as a pointer to a closure, like a Box or an Rc. This would help us to define actions more flexibly, without having to create a special structure for each one.
Taking input parameters
For buttons, menu items, and keyboard shortcuts, it's useful to have a kind of action that applies a fixed procedure to a reactive input parameter. This factoring makes it easier to reuse the same procedure for command language interpretation, where the input parameter is more tightly bound to the procedure and is known at the time when the action is constructed.
In the current draft, this kind of action is represented by the ParametricAction<T> structure, where:
- The input parameter is a
ReadSignalwith a value of typeResult<T, String>. - The procedure is a boxed closure that maps
Tvalues to boxedActiontrait objects.
The type parameter T lets us enforce type consistency between the input parameter and the procedure at compile time. Since the input parameter has type Result<T, String>, we still have room to produce the input parameter through runtime type conversion, setting the input parameter to Err if conversion fails.