Cute UI

June 13, 2026

Abstract

This document defines a UI framework specification, focused on low-end or old mobile devices.

Terminology

For the purposes of this document, we define:

Rect (rectangle) — a combination of XY and WH.

WH — width and height (short).

XY position (short).

Range — numeric range from min to max.

Tick (UI thread tick, UI tick) — fixed period for UI recalculations and updates (tens of milliseconds).

Root (root component) — the main component that is fitted to the screen size.

Component API

Generic component

Each component in Cute UI descends from a Component class, which defines:

  • component's own WH preferences (min and max)
  • paddings and margins
  • measured WH of the component
  • calculated rect of the component
  • flags tracking current layout and paint validity
  • flags tracking interaction state: focus and activeness (enable/disable)
  • measure, layout (no-op for non-containers) and paint methods
  • means of tracking the parent
  • invalidation methods
  • event handler method (no-op default), hence making it a descendant of Event listener

Invalidation methods, when called, should properly propagate the invalidation up the hierarchy to the root component.

Direct use of measured WH, writes to calculated rect, writes to validity flags bypassing the invalidation methods as well as direct calls of the rendering pipeline methods is highly discouraged up to the point of recommending hiding these APIs with target language's means of information hiding and access control. Unregulated use of these may lead to misbehaviour of the framework.

Generic container component

A container component additionally defines:

  • layout strategy
  • children array
  • methods for manipulating children

The children manipulation methods implementation should responsibly make use of invalidation methods of the Component API in order to make the changes visible in time.

The component's implementation should notify the component when it becomes a child of this container via calling the corresponding method or setting the corresponding field. The same holds for child removal.

The container component's implementation generally should delegate the measurement and layout phases to the layout strategy assigned to this container.

Layout strategy

The layout strategy fully defines the layout and measurement phases of the components it is assigned to. It should measure and lay out all the children. The layout strategy is the only object allowed to use the measured WH and write calculated rect.

This pattern allows for flexible layouts to be made.

Event listener

An event listener object that has a method to handle events.

Rendering process

The rendering process is split into three phases:

  • measure
  • layout
  • paint

Each tick if the root component is marked as layout-invalid, the renderer will call the measurement method on the root component, then the layout and then the paint method on root is triggered. Each tick if the root component is marked paint-invalid, it is repainted, but the measurement and layout phases are skipped.

When measurement and layout are done, the component is marked as layout-valid. When the painting is done, the component is marked as paint-valid.

If the component is fully valid, no action occurs until next tick.

Measurement

In the measurement phase the components try to determine their actual widths and heights based on current UI structure. At this phase components can do stateful calculations in order to fulfil the requirements. For example, text components can run algorithms arranging the line breaks and store the result to use it later in the paint phase.

Note however, that the measurement method should be idempotent in a sense that without any external changes to the component contents or other its properties, the results of calling it multiple times are consistently producing the same output.

In order to correctly determine width and heights, the measurement method accepts WH ranges this component can ever take. These originate from the root (the physical screen constraints) and are passed down the tree. The components are allowed to modify the constrains in any way they like. For example, a ScrollView might want to set one or both range maximums to infinity.

After the measurement method is completed, the component's implementation should guarantee that:

  1. Children measurements were done (if any)
  2. The measured WH are stored in this component (as per Component API definition)

Layout

After the measurement of the root is completed, the UI begins the layout process. In this phase all elements get their actual positions and sizes (calculated rect). Note that the XY of it is not absolute, but rather relative to the component's parent.

Generally, it's discouraged to perform any stateful calculations in this phase except for ones that directly involve the manipulations with measured WH and calculated rect. Note that the layout method should be idempotent in a sense that without any external changes to the component contents or other its properties, the results of calling it multiple times are consistently producing the same output.

Each component's implementation should guarantee that:

  1. All children layouts were done (if any)
  2. The calculated rect is stored in this component (as per Component API definition)

Paint

The last rendering phase is painting everything onto the screen according to calculated rect as per Component API. Note that other sizes and positions mean nothing in this phase: you cannot rely on measured WH, use only calculated rect for painting, as it reflects the actual consensus in the layout between the components.

In this phase it is strictly forbidden to cause any changes to the component whatsoever without any exception for any of its fields and methods.

Each component's implementation should guarantee that:

  1. The component is fully painted
  2. All children are fully painted (if any)

Note that it's generally discouraged to paint children which happen to be off the viewport and/or screen. Check their actual position using the calculated rects.

Event processing

It is a concrete architectural decision to ignore the classical Observer pattern and passing events as objects. In given environment, these approaches share a big problemL: creating too many small short-lived objects. This, tied with very low performance of the target devices and garbage collector activity, may lead to severe performance degradation. Hence a more lightweight yet less expressive approach has been chosen.

The events we want to track are:

  • keyboard presses and releases
  • touchscreen "pointer" up, down and drag events

These events are characterised by:

  • event type
  • key code or coordinate

As these are all events, we can safely assume we can code each event as 3 integers:

  • type
  • arg1
  • arg2

As per the Component API, the event handler method will be called if the UI will consider it needed. Only the root component event handler is called upon event. The responsibility of forwarding the event down the hierarchy lies on the component's implementation.

Different components may generate their own events. For these purposes it is encouraged to leave an additional field for an Event listener object that will handle these new events.

Traversal

As the target device may be a non-touch capable feature-phone, the framework should have means of navigating using only the D-pad.

??? global traversal array ???

??? enableTraverse / disableTraverse methods ???