Skip to content
Lautaro Brasseur edited this page Jul 2, 2018 · 5 revisions

Architecture

This section explain the framework low-level design.

API / Implementation

The org.nibiru.ui.core project provides basic interfaces and generic classes. For each platform, there is a project containing implementations that are specific to the given platform. The name convention is similar (org.nibiru.ui.android, org.nibiru.ui.ios, org.nibiru.ui.gwt, etc.)

All the widgets are defined in org.nibiru.ui.core.api package. Such package provides generic interfaces for each component.

Implementations for each widget are found on projects specific for each platform, although there are also generic widgets defined in the core project.

Dependency Injection / Inversion Of Control

The glue for all the components is the dependency injection pattern. The classes are configured using JSR330 annotations. This allows using any dependency injection framework. For simplcity, Dagger 2 configuration is provided. Since Dagger 2 uses a code generation approach (no reflection is needed), it can be used on all the platforms.

Each time you need to instantiate a component, you should just inject a javax.inject.Provider<T>, where T is the widget type. In some specific cases (ComboBox and RadioButtonGroup), you may need to inject a factory, in order to keep type safety.

Each widget has a asNative() method that returns the native widget. You will need to use this method to add the widget/panel to the native container (usually a root container or panel).

Builder-based DSL

Usually, you will not need to inject a Provider for a widget. Instead, you will use a builder. The builder internally uses a Provider to instantiate the widget, but provides methods for allowing a less verbose widget creation. This is the internal DSL (Domain Specific Language) for UI creation.

The DSL takes advantage of the Java type system to ensure that each parameter set has a correct type.

The DSL fits well with the compound model for the UI. In many cases you will be able to define the UI in a single sentence, such as in this example:

@Inject
public LoginDisplay(UiBuilder uiBuilder,
                    UiMessages uiMessages,
                    Styles styles) {
    super(uiBuilder, uiMessages);
    TextStyle itemStyle = textStyle()
            .horizontalAlignment(Alignment.CENTER)
            .fontSize(20)
            .marginTop(10)
            .marginBottom(10)
            .marginLeft(30)
            .marginRight(30)
            .build();
    TextStyle allCapsItemStyle = textStyle()
            .parent(itemStyle)
            .allCaps(true)
            .build();
    container = relativePanel()
            .style(styles.container())
            .prepare(image()
                    .style(styles.backgroundImage())
                    .build("background.jpg"))
                .alignLeft()
                .alignTop()
                .matchRightWithRight()
                .matchBottomWithBottom()
                .add()
            .prepare(verticalPanel()
                    .style(style()
                            .backgroundColor(Color.WHITE)
                            .build())
                    .add(label()
                            .style(allCapsItemStyle)
                            .build(uiMessages.welcomeTo()))
                    .add(image()
                            .style(itemStyle)
                            .build("AAJLogo.jpg"))
                    .add(label()
                            .style(textStyle()
                                    .parent(allCapsItemStyle)
                                    .fontSize(40)
                                    .build())
                            .build(uiMessages.hi5()))
                    .add(label()
                            .style(textStyle()
                                    .parent(itemStyle)
                                    .marginTop(40)
                                    .build())
                            .build(uiMessages.signIn() + ":"))
                    .add(login = button()
                            .style(styles.primaryButton(itemStyle))
                            .value(uiMessages.office365())
                            .build())
                    .build())
                .centerHorizontally()
                .centerVertically()
                .add()
            .build();
}

In some cases (such as circular dependencies), you may need to use intermediate variables.

Bindings

Component properties are exposed as Nibiru Model objects. This allows implementing type-safe bindings. Please refer to Nibiru Model documentation.

Layouts

Layouts are implemented in a generic way. Each platform just provides an implementation of an absolute layout. More complex layouts (vertical, horizontal, relative, grid, etc.) are implemented using generic components. This way, the layout behavior is unified across the different platforms. Also, it enables implementing some features not available on certain platforms, such as relative layouts or styles.

TODO: Explain the layout algorithm.

Styles

The style framework provides a cross-platform, type safe way to define styles. Since styles are defined using Java classes, you can manage instances as you want (for example, singleton instances for shared styles). You can also take advantage of inheritance.

Custom components

Since the entire framework is based on dependency injection, building new components is really easy.

Custom Widgets

Custom Layouts

Technology stack