The Ingredients
OrbTk
provides an interactive functional
reactive API. It depends on the rust
DCES
crate, that provides an Entity Component
System. Interaction with DCES
is managed via the Entity Component Manager
(ECM), a wrapper API, that transparently maps OrbTk
widgets
to ECM
entities and OrbTk
properties to ECM
components.
The widget view
graph TD; View-->Widget-Container_1; View-->Widget-Container_2; Widget-Container_1-->Child-Widget1_1; Widget-Container_1-->Child-Widget1_2; Widget-Container_1-->Child-Widget1_n; Widget-Container_2-->Child-Widget2_1; Widget-Container_2-->Child-Widget2_2; Widget-Container_2-->Child-Widget2_n;
Workflow 1-1: View handling methods
When you construct an OrbTk application, you are essentially combining
widgets
. Widgets are the building blocks of user interfaces in
OrbTk, dedicated to a given task. The given model is dynamically
structured. You are free to implement your own widget type or consume
the predefined implementations.
To make up a widget-tree
, you may construct a ListView
, that
instantiates a child TextBox
that itself instantiates a child
Button
. You end up with an ordered user interface (the view
), the
visible part of your app. Each widget-tree is stacked into a uniquely
addressable widget-container
.
Widget trait
Each widget need to implement the Widget trait. It is generated by the
widget!();
macro.
A widget consists of a name (eg. Button
) that is bound to a list of
properties (eg. text: String
, background: Brush
or count: u32
). When the build method
of a widget is called inside the
widget-tree, that widget is added to the Entity Component System with
a unique Entity
(index) that holds corresponding Components
(property
names). The struct of the widget-container serves as its builder pattern.
Widget Template
Each widget has to implement the Template
trait. The template defines
the default values of a widget’s properties as well as its
structure. E.g. a Button consists of a Container widget, a StackPanel
widget and a TextBox widget.
Separating the view
as the descriptive nature of a widget tree from
the code that reacts and handles user input (it’s state
), is an
essential conceptual decision. It is key to enable the fast, flexible
and extendable structure of OrbTk.
The widget state
graph TD; State-->init; State-->update; State-->cleanup; update-->message; message-->layout; layout-->update_post_layout;
Workflow 1-2: State handling methods
Widgets make use of traits, that come in handy to provide
interactivity. We call them the widget state
. Inside the state
routines, we declare the processing and controlling code dedicated to
a given task. The given model is dynamically structured. You are free
to implement your own widget type or consume the predefined
implementations.
It is not required to define a state
for a widget. But if you
don’t, you cut of the possibility to adapt properties during
runtime. The view
of the widget will stay static.
When defining a state
of a widget, it inherits the values of its
associated properties (current values
), as well as the implemented
system. To gain access, each state has to derive or implement the
[Default
] and the [AsAny
] traits. You are free to implement
associated functions to the state
, that react on triggered events or
adapt current values. The properties
are stored via ECM. They are
organized in a tree (parent, children or level entities).
GUI Elements
- Layouts
- Events
- Behaviors
- Messages
Layouts
Layouts are addressing the problem, that each widget inside the UI needs individual placement. This requires a dynamic calculation of its space requirements coupled with is target specific positioning when interacting with the display device.
Why do we need layouts?
Well, lets take an obvious example that meets every modern application: You have to support multiple language variants! Changing the selected localization should be possible at runtime. We do expect, that each needed idiom inside our application will for sure differ in their label length, as well as the sizes of used glyphs in the selected fonts. What would happen, if we would size the entities statically? We would code e.g a button with a given size. How to react on content changes of its child entity (e.g. a label that is expected to be centered inside the button frame)?
Pugh, you as the programmer would need to adapt the GUI views for every supported language and react on pragmatically on any of this size changes. A nightmare! This is nonsense. We have to define and render the stuff the other way around!
Our solution
OrbTk
uses a layout
system. This system support the ability to
determine the natural size of the content elements. It allows a dynamic
approach to layout all entities inside the toolkit. No matter if the
application logic requires to add or subdivide other entities inside
the widget tree. Or if contents is changed through theme adaption or
user interaction: all involved entities are processed and resized as
needed, respecting their individual constraints.
Constraints are defined via entity properties that are
stored as components inside the DCES
. The concept follows a two
phase model. It will process the entity tree in two passes:
Measuring
passArrangement
pass
This is the reason, we call it a functional_reactive
Toolkit.
Measuring
The Measuring
pass allows us to determine the desired size of a
boxed
entity. A desired size is a structure, that holds the maximum
values for the width and height of the entity in question. This values
are stored inside DCES
. If computation recognizes a size change of
the desired size
, which means the stored and the current value of
its property differs, the dirty flag
is set. The measuring will
result in an update of the associated bound values inside the DECS
entity (structure desired size).
Arrangement
The Arrangement
is following in a separate run. It will trigger the
2D rendering task. This task walks down the element tree and consume
the bounds of each entry. A bound
describes the finalized
alignment position of an entity (height, width) and is stored inside
the DCES
. Computation tasks are only triggered, if the values of
a tree element have changed, which will be indicated via the dirty
flag. All referenced elements that are affected by this changed values
need to be rearranged. Their positions are recomputed with the
appropriate values inside the render buffer, since the active state
was marked dirty.
After the arrangement pass, the dirty flag is cleaned, which will omit any further computational needs. Once the state of an entity is marked as dirty again, the pass runs are triggered as desired.
Layout Methods
OrbTk
supports a number of dedicated layout methods, that are
designed to handle the specific demands of a given widget type:
- Absolute
- Fixed size
- Grid
- Padding
- Popup
- Stack
You can find the relevant code inside the orbtk_core
workspace. The methods are inside the sub-directory layout
.
Further information towards this methods are discussed in Chapter: Ortk_core.
Events
- bottom-up
If the events traverse from a leaf entity up to the root entity.
- top-down
If the events traverse from the root entity down to the leaf entities.
Behaviours
Specialized event handling that reacts on signals. Signals may be fired from ether input devices (e.g. mouse, keyboard) or inside the functional logic (e.g. changing focus, text, etc).
Messages
An intelligent messaging infrastructure that instantiates subs. The concept enables the toolkit to send and receive messages between the linked entities (m senders -> n receivers).
Framework Elements
The elements are organised as sub-modules inside the API sub-tree.