Components¶
Overview¶
Sedona is a component oriented language, which means that programs are developed as reusable chunks of code designed to be snapped together much like Lego building blocks. This chapter discusses components themselves; the Apps chapter discusses how components are assembled into applications.
A component is any class that extends sys::Component
. Components
include several features:
- Can define property fields
- Can define action methods
- Can be organized into a tree structure
- Can be given a human friendly name (up to 7 ASCII characters)
- Have a two byte identifier within an application
- Can be linked within an application
- Provides callback methods that can be overridden
- Component types have reflection capability
Properties¶
Properties are normal instance fields that are annotated with the
property
keyword. Properties are how components expose configuration
and runtime state. Properties are restricted to the following types:
bool
byte
short
int
long
float
double
sys::Buf
Note that you must use the := operator when assigning values to properties. See Assigning to Properties for details.
Config vs Runtime¶
Properties are either config or runtime. Config properties are persistent and typically are changed by the user. Runtime properties are transient and are changed by the component itself during runtime. Properties are runtime by default unless marked with the "config" facet. A simple example:
class FooServer extends Component
{
@config property bool enabled
@config property int port = 80
property int count // runtime prop
}
Buf Properties¶
Properties typed as Buf
are a bit special from the primitive property
types. The biggest difference is that primitives are by-value and can't
be changed without setting the field itself. However, buffer properties
are always inlined and accessed by reference. When declaring a buffer
property, the inline
modifier is implied. The following example
illustrates a buffer property with a total capacity of 8 bytes:
class Foo extends Component
{
property Buf(8) blob
}
Because buffer properties are accessed by reference, the framework has
no knowledge when the buffer is modified. So it is the developer's
responsibility to notify the framework of a buffer change via the
Component.changed
method (typically using a slot literal):
void updateBlob()
{
blob.copyFromStr("wow!")
changed(Foo.blob)
}
Note that changed
is called automatically when setting primitive
property types. You should only manually call changed
for Buf
properties.
AsStr Properties¶
Buf
is the only non-primitive property type available. However, it is
a common case to store a Str
value in a Buf
property. If you know
that a buffer property will always store a null-terminated string, then
you should mark the property with the "asStr" facet:
class Foo extends Component
{
@config @asStr property Buf(8) descr
}
The Buf
size is the total capacity including the null terminator - so
in the example above we can store a string with a max of seven
characters. The Buf
class has convenience methods when storing a
string such as toStr
and copyFromStr
.
Marking a buffer property asStr has a couple of benefits:
- Buffer overruns are handled to ensure that there is always a null terminator during deserialization.
- It lets higher levels of the framework treat the buffer as more than raw binary data. For example, asStr properties are serialized as text rather than base64 in XML files.
Actions¶
Actions are normal instance methods that are annotated with the action
keyword. Every action is implicitly defined to be virtual
, so they can
be overridden. Actions are typically used as commands on a component. As
methods, actions "do something" rather than store or display a value.
Actions methods must be declared to return void and must have zero or
one parameter. If a parameter is specified then it must be one of the
following types:
bool
int
long
float
double
sys::Buf
Examples of "actions in action":
class Foo extends Component
{
action void actionNoArg()
{
Sys.out.print("actionNoArg").nl()
}
@asStr action void actionStr(Buf x)
{
Sys.out.print(x.toStr()).nl()
}
}
class FooOverride extends Foo
{
override action void actionNoArg()
{
Sys.out.print("Override actionNoArg").nl()
}
}
Note that you can use the "asStr" facet to annotate an action that takes a Buf, if the argument should be a null-terminated string.
Callbacks¶
Any class that extends sys::Component
includes the following virtual
methods. Each is called automatically by the system at the appropriate
time. These callback methods may be overridden by Component subclasses
to change the behavior of the component.
virtual void loaded()
virtual void start()
virtual void execute()
virtual void changed(Slot slot)
virtual void setToDefault(Slot slot)
virtual int childEvent(int eType, Component child)
virtual int parentEvent(int eType, Component parent)
virtual int linkEvent(int eType, Link link)
See the API documentation at sys::Component
for details on how these
methods are used.