Your own application

Developer Documentation for the org.luwrain.app.base Package

This is the developer documentation for the org.luwrain.app.base package. This package provides a simplified framework for building applications in the LUWRAIN platform, which allows constructing cross-platform interfaces for blind people.

The architecture of a LUWRAIN application using this package revolves around two primary classes:

  1. org.luwrain.app.base.AppBase: The main entry point and lifecycle manager of the application.
  2. org.luwrain.app.base.LayoutBase: The UI builder that manages areas, input events, and system actions.

To create a new application, you must extend the AppBase class. It requires a generic type representing your localized strings class (for internationalization).

You must override the onAppInit() method. This method is called when the application starts. Here, you should initialize your configuration, instantiate your main layout, set the application name, and return the initial AreaLayout.

public class App extends AppBase<Strings>
{
    private MainLayout mainLayout;

    public App()
    {
        super(Strings.class, "my.app.help");
    }

    @Override
    protected AreaLayout onAppInit()
    {
        // Initialize layout
        this.mainLayout = new MainLayout(this);
        
        // Set the application name
        setAppName(getStrings().appName());
        
        // Return the layout
        return mainLayout.getAreaLayout();
    }

    @Override
    public boolean onEscape()
    {
        closeApp();
        return true;
    }
}

The LayoutBase class provides a convenient way to construct user interfaces. It acts as a wrapper for standard LUWRAIN controls like EditArea, ListArea, ConsoleArea, and TreeArea.

Instead of instantiating parameter objects manually, LayoutBase provides builder methods with lambda expressions, such as editParams, listParams, and consoleParams.

class MainLayout extends LayoutBase
{
    final EditArea editArea;

    MainLayout(App app)
    {
        super(app);
        
        this.editArea = new EditArea(editParams(p ->
	{
            p.name = app.getStrings().appName();
            // Configure other parameters here
        }));
        
        // Set the layout and bind actions
        setAreaLayout(editArea, actions(
            action("save", "Save File", new InputEvent(InputEvent.Special.F2), this::actSave),
            action("close", "Close", new InputEvent(InputEvent.Special.ESCAPE), () -> {
                app.closeApp();
                return true;
            })
        ));
    }

    private boolean actSave()
    {
        app.message("File saved!", Luwrain.MessageType.OK);
        return true;
    }
}

Actions and Event Handling

Actions are defined using the action method and grouped using the actions method. An action binds a specific InputEvent (like a keyboard shortcut) to a handler function.

You can define complex shortcuts using modifiers:

action("open", "Open File", new InputEvent(InputEvent.Special.F3, EnumSet.of(InputEvent.Modifiers.SHIFT)), this::actOpen)

Properties and Settings Layouts

You can easily attach a properties layout (e.g., a settings menu) to an area using the setPropertiesHandler method. When a user triggers the properties event, the specified layout will open.

setPropertiesHandler(editArea, area -> new SettingsLayout(app, getReturnAction()));

The getReturnAction method provides a callback to return to the current layout once the settings menu is closed.

Managing Background Tasks

LUWRAIN applications often need to perform network requests or heavy computations (like AI queries). The AppBase class extends TaskCancelling to handle this safely without freezing the UI.

Use the newTaskId() method to generate a unique identifier for the task, and runTask() to execute it. Once finished, use finishedTask() to safely update the UI on the main thread.

private boolean performAiQuery(String prompt)
{
    if (app.isBusy()) return false;

    final var taskId = app.newTaskId();
    
    return app.runTask(taskId, () -> {
        // Background thread: perform heavy operation
        String result = myAiClient.query(prompt);
        
        app.finishedTask(taskId, () -> {
            // UI thread: update the interface
            editArea.update((lines, hotPoint) -> {
                lines.setLines(result.split("\n"));
                return false;
            });
            app.getLuwrain().playSound(Sounds.DONE);
        });
    });
}

You can cancel an ongoing task using the cancelTask() method, which is automatically bound to the ESCAPE key in AppBase if a task is currently running.