---
title: User Interface (UI) — Layer 5
aliases: [UI, user interface, UIDescription, screen, layout]
sources:
    [
        sources/sessions/2026-02-18-ui-analysis.md,
        sources/sessions/2026-04-20-ui-patterns-and-bl-apis.md,
        sources/sessions/2026-04-21-calendar-control-deep-dive.md,
        sources/sessions/mfg-app/2026-04-22-build-error-learnings.md,
    ]
last_updated: 2026-04-22
status: draft
---

# User Interface (UI) — Layer 5

UI definitions describe screen layout, data bindings, responsive layouts, and user interaction events.

## Page Patterns

| Pattern                   | Use                        | Key Attribute             |
| ------------------------- | -------------------------- | ------------------------- |
| `SingleSectionPage`       | Standard full page         | Default for most screens  |
| `SingleSectionDialogPage` | Dialog-style modal         | `onBackDiscard="true"`    |
| `TabbedSectionPage`       | Not commonly used directly | See TabbedViewAreaSection |

### TabbedViewAreaSection (Tabs within a Page)

```xml
<Page pagePattern="SingleSectionPage" cachable="false">
  <Section sectionName="masterSection" sectionPattern="TabbedViewAreaSection"
           currentTab="ProcessContext::CurrentTabName">
    <Area areaName="tabArea" areaPattern="TabElementArea">
      <TabSelector name="TabSelector">
        <Items>
          <Tab tabName="Details" backendSystem="both">
            <Bindings>
              <Resource target="image" type="Image" id="Icon24" />
              <Resource target="text" type="Label" defaultLabel="Details" />
            </Bindings>
          </Tab>
          <Tab tabName="Items" backendSystem="both">
            <Bindings>
              <Resource target="image" type="Image" id="ListIcon24" />
              <Resource target="text" type="Label" defaultLabel="Items" />
            </Bindings>
          </Tab>
        </Items>
        <Events>
          <ItemSelectedEvent event="tabSelected" />
        </Events>
      </TabSelector>
    </Area>
    <!-- Content areas match tabName -->
    <Area areaPattern="MultiArea" areaName="Details">
      <Area areaName="Details" areaPattern="GroupedElementsArea">
        <!-- Tab content -->
      </Area>
    </Area>
  </Section>
</Page>
```

## Area Patterns

| Pattern               | Use                                    |
| --------------------- | -------------------------------------- |
| `SingleElementArea`   | One element fills the area (list, map) |
| `GroupedElementsArea` | Multiple GroupElement form sections    |
| `TabElementArea`      | Contains TabSelector for tabs          |
| `MultiArea`           | Contains nested areas (tab content)    |

## PageHeader & MenuItems

```xml
<PageHeader>
  <Bindings>
    <Binding target="title" binding="ProcessContext::DetailBo.name" />
  </Bindings>
  <MenuItems>
    <MenuItem directlyVisible="true" itemId="save">
      <Bindings>
        <Resource target="Text" type="Label" defaultLabel="Done" />
        <Resource target="Icon" type="Image" defaultImage="light/done_24.png" />
        <Binding type="Editable" target="Editable"
                 call="ProcessContext::DetailBo.isSaveEnabled" />
        <Binding type="Visible" target="Visible"
                 call="ProcessContext::DetailBo.isSaveButtonVisible" />
      </Bindings>
      <Events>
        <ButtonPressedEvent event="saveItem" />
      </Events>
      <VisibilityRoles allRoles="false">
        <Role name="RetailUser" />
      </VisibilityRoles>
    </MenuItem>
  </MenuItems>
</PageHeader>
```

-   `directlyVisible="true"` — shows in header bar; `false` — overflow menu
-   `type="Editable"` binding — enables/disables (greyed out when "0")
-   `type="Visible"` binding — shows/hides entirely
-   `<VisibilityRoles>` — role-based access control

## Form Controls

### GroupElement (Form Section Container)

```xml
<GroupElement name="InfoGroup">
  <Bindings>
    <Resource target="Title" type="Label" defaultLabel="Information" />
  </Bindings>
  <!-- Form fields go here -->
</GroupElement>
```

### InputArea (Text Field)

```xml
<!-- Editable -->
<InputArea name="Name">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="Name" />
    <Binding target="Value" binding="ProcessContext::Bo.name" bindingMode="TWO_WAY" />
  </Bindings>
</InputArea>

<!-- Read-only -->
<InputArea name="Id" disabled="true">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="ID" />
    <Binding target="Value" binding="ProcessContext::Bo.id" bindingMode="ONE_WAY" />
  </Bindings>
</InputArea>
```

### DatePickerField & TimePickerField

```xml
<DatePickerField name="StartDate" dateVisible="true" timeVisible="false">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="Start Date" />
    <Binding target="Value" binding="ProcessContext::Bo.startDate" bindingMode="TWO_WAY" />
  </Bindings>
</DatePickerField>
```

### Merger (Two Fields Side-by-Side)

```xml
<Merger name="DateTimeMerger" pattern="twoInputControls"
        labelHandling="Own" leftRatio="1" rightRatio="1">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel=" " />
  </Bindings>
  <DatePickerField name="StartDate">
    <Bindings>
      <Binding target="Value" binding="ProcessContext::Bo.startDate" bindingMode="TWO_WAY" />
    </Bindings>
  </DatePickerField>
  <TimePickerField name="StartTime">
    <Bindings>
      <Binding target="Value" binding="ProcessContext::Bo.startTime" bindingMode="TWO_WAY" />
    </Bindings>
  </TimePickerField>
</Merger>
```

### SelectionBox (Dropdown)

```xml
<SelectionBox name="Category" editable="true">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="Category" />
    <Binding target="DataSource" toggleId="CategoryToggle" bindingMode="ONE_WAY" />
    <Binding target="Value" binding="ProcessContext::Bo.category" bindingMode="TWO_WAY" />
  </Bindings>
  <Items>
    <Bindings>
      <Binding target="ItemValue" type="Text" binding=".id" bindingMode="ONE_WAY" />
      <Binding target="ItemText" type="Text" binding=".text" bindingMode="ONE_WAY" />
    </Bindings>
  </Items>
  <Events>
    <ItemSelectedEvent event="categorySelected" />
  </Events>
</SelectionBox>
```

### Stepper (Numeric with +/- Buttons)

```xml
<Stepper name="Discount" minValue="0" maxValue="100">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="Discount %" />
    <Binding target="Value" type="Decimal" formatV2="3.1"
             binding="ProcessContext::Bo.discount" bindingMode="TWO_WAY" />
    <Binding target="StepSize" value="0.1" />
  </Bindings>
</Stepper>
```

### Lookup (Reference Field)

```xml
<Lookup name="Customer" disabled="true">
  <Bindings>
    <Resource target="Label" type="Label" defaultLabel="Customer" />
    <Binding target="Value" binding="ProcessContext::Bo.luCustomer.name" bindingMode="ONE_WAY" />
  </Bindings>
</Lookup>
```

## List Controls

**Build-critical rules for all list controls:**

1. `dataSource` uses capital-I `.Items[]` (e.g., `ProcessContext::ItemList.Items[]`). Lowercase `.items[]` may cause silent failures.
2. Items MUST be wrapped in `<Items name="Items">` — placing `<ItemListLayout>` directly inside the list element causes error `00000001`.
3. Column layout uses `bindingId` attributes on `<Col>` (not nested `<Binding>` elements). The `bindingId` references are then resolved by `<Bindings>` entries inside the same `<Items>`.
4. GroupedList uses `groupBy` attribute (not `groupedAttribute`). The `groupedAttribute` name does not exist in the schema.
5. Process flow entry requires `<EntryActions />` even if empty — omitting it causes `"Missing child element(s)"` error.

### GroupedList (Standard Data List)

```xml
<GroupedList name="MasterList" dataSource="ProcessContext::ItemList">
  <Items name="Items" itemPattern="ListPattern">
    <ItemListLayout>
      <Default>
        <Col width="1.7em" height="1.4em" layoutType="Image" bindingId="Icon" />
        <Col flex="1">
          <Row layoutType="itemIdentifier" bindingId="Name" />
          <Row>
            <Col layoutType="itemSecondary" bindingId="Date" />
            <Col layoutType="itemValue" bindingId="Status" />
          </Row>
        </Col>
      </Default>
      <Tablet><Default>
        <!-- Tablet layout -->
      </Default></Tablet>
      <Phone><Default>
        <!-- Phone layout (more compact) -->
      </Default></Phone>
    </ItemListLayout>
    <Bindings>
      <Binding target="Icon" type="Image" imageType=".svg" binding=".statusIcon" bindingMode="ONE_WAY" />
      <Binding target="Name" type="Text" binding=".name" bindingMode="ONE_WAY" />
      <Binding target="Date" type="Date" binding=".dueDate" bindingMode="ONE_WAY" />
      <Binding target="Status" type="Text" binding=".status" bindingMode="ONE_WAY" />
    </Bindings>
    <Events>
      <ItemSelectedEvent event="itemSelected">
        <Params>
          <Param name="pKey" value=".pKey" />
        </Params>
      </ItemSelectedEvent>
    </Events>
  </Items>
</GroupedList>
```

### MultiSelectionGroupedList (with Stepper in Items)

```xml
<MultiSelectionGroupedList name="OrderItems" numberpadDefaultField="quantity"
    showPreviousNextEnter="true" searchable="true" sortBy="groupId" direction="ASC"
    dataSource="ProcessContext::MainBO.LoItems.Items[]" master="true">
  <SearchAttributes>
    <SearchAttribute name="text1" />
    <SearchAttribute name="eAN" />
  </SearchAttributes>
  <Items name="Items">
    <Bindings>
      <Binding target="Quantity" type="Stepper" binding=".quantity"
               minValue="0" maxValue="9999" bindingMode="TWO_WAY" formatV2="4.0"
               stepperCorrelationId=".pKey" />
    </Bindings>
  </Items>
</MultiSelectionGroupedList>
```

### CockpitList (Card List)

```xml
<CockpitList name="CardList" hasBorder="false"
             dataSource="ProcessContext::Card_List.Items[]">
  <Items name="Items">
    <ItemListLayout>
      <Default>
        <Col width="1.5em" height="1.5em" layoutType="Status" bindingId="StatusIcon" />
        <Col flex="1">
          <Row layoutType="itemIdentifierCockpit" bindingId="Name" />
        </Col>
      </Default>
    </ItemListLayout>
    <Bindings>
      <Binding target="Name" type="Text" binding=".name" bindingMode="ONE_WAY" />
      <Binding target="StatusIcon" type="Image" binding=".statusIcon" bindingMode="ONE_WAY" />
    </Bindings>
    <Events>
      <ItemSelectedEvent event="Card_itemSelected">
        <Params>
          <Param name="pKey" value=".pKey" />
        </Params>
      </ItemSelectedEvent>
    </Events>
  </Items>
</CockpitList>
```

## Layout System (Col/Row)

| Attribute        | Purpose                          | Example         |
| ---------------- | -------------------------------- | --------------- |
| `width="10em"`   | Fixed width column               | Labels, icons   |
| `height="1.5em"` | Fixed height                     | Icon containers |
| `flex="1"`       | Flexible width (fills remaining) | Content columns |
| `layoutType`     | Visual treatment                 | See below       |

### layoutType Values

| Value                   | Purpose                    |
| ----------------------- | -------------------------- |
| `itemIdentifier`        | Primary text (bold)        |
| `itemIdentifierSmall`   | Smaller primary text       |
| `itemIdentifierCockpit` | Cockpit card primary text  |
| `itemValue`             | Value text (right-aligned) |
| `itemSecondary`         | Secondary/subtitle text    |
| `itemLabel`             | Label text (grey)          |
| `itemLeft`              | Left-aligned override      |
| `itemRight`             | Right-aligned override     |
| `Image`                 | Image container            |
| `Status`                | Status icon container      |

## Binding Modes

| Mode       | Direction            | Use Case                   |
| ---------- | -------------------- | -------------------------- |
| `ONE_TIME` | Read once            | Static labels, page titles |
| `ONE_WAY`  | BO → UI (read-only)  | Display fields, list items |
| `TWO_WAY`  | BO ↔ UI (read/write) | Editable form fields       |

## Binding Paths

| Pattern                                    | Context           | Example                              |
| ------------------------------------------ | ----------------- | ------------------------------------ |
| `ProcessContext::VarName`                  | Process variable  | `ProcessContext::CardDate`           |
| `ProcessContext::BoName.property`          | BO property       | `ProcessContext::Bo.name`            |
| `ProcessContext::BoName.luLookup.property` | Lookup property   | `ProcessContext::Bo.luCustomer.name` |
| `.propertyName`                            | Current list item | `.amount`, `.statusIcon`             |

## Visibility & Editability

### Method-Based Visibility (call attribute)

```xml
<Binding type="Visible" target="Visible"
         call="ProcessContext::CardController.isCardVisible" bindingMode="ONE_WAY">
  <Parameters>
    <Input name="cardName" type="Literal" value="CardVisits" />
  </Parameters>
</Binding>
```

### Method-Based Editability

```xml
<Binding type="Editable" target="Editable"
         call="ProcessContext::MainBO.isSaveEnabled" />
```

### Role-Based Visibility

```xml
<VisibilityRoles allRoles="false">
  <Role name="RetailUser" />
  <Role name="TourUser" />
</VisibilityRoles>
```

## Format Patterns

```xml
<!-- Decimal: 10 total digits, 2 decimal places → "1,000.00" -->
<Binding type="Decimal" formatV2="10.2" />

<!-- Date -->
<Binding type="Date" binding=".dueDate" />

<!-- Image (SVG) -->
<Binding type="Image" imageType=".svg" binding=".iconName" />

<!-- Combo (Toggle/Picklist) -->
<Binding type="Combo" binding=".status" toggleId="StatusToggle" bindingMode="TWO_WAY" />
```

## Events

### Button Events

```xml
<Events>
  <ButtonPressedEvent event="saveItem" />
</Events>
```

### List Events with Params

```xml
<Events>
  <ItemSelectedEvent event="itemSelected">
    <Params>
      <Param name="pKey" value=".pKey" />
      <Param name="status" value=".status" />
    </Params>
  </ItemSelectedEvent>
</Events>
```

### Context Menu Events

```xml
<Events>
  <ContextOpeningEvent event="contextMenuOpening">
    <Params>
      <Param name="pKey" value=".pKey" />
      <Param name="visitStatus" value=".status" />
    </Params>
  </ContextOpeningEvent>
  <ContextSelectedEvent event="contextMenuItemSelected">
    <Params>
      <Param name="pKey" value=".pKey" />
    </Params>
  </ContextSelectedEvent>
</Events>
```

## Best Practices

| Do                                                         | Don't                             |
| ---------------------------------------------------------- | --------------------------------- |
| Use `ONE_WAY` for read-only fields                         | Use `TWO_WAY` on disabled fields  |
| Define all three responsive layouts (Phone/Tablet/Default) | Only define Default layout        |
| Localize all text with `<Resource>` and ID                 | Hardcode display strings          |
| Use `formatV2` for decimal/currency display                | Let raw numbers display           |
| Use `call=` for dynamic visibility/editability             | Hardcode visibility               |
| Use `<Params>` on events to pass item data                 | Rely on global state              |
| Use `directlyVisible="true"` for primary actions           | Hide critical actions in overflow |

## CalendarControl

The modeler includes a **native CalendarControl** with weekly/daily views, drag-and-drop (move, resize, sidebar drop), context menus, and configurable user settings. See [[calendar-control]] for full documentation.

## Cross-References

-   [[processes]] — UI is bound to ProcessContext from the Process; events defined here
-   [[business-objects]] — UI displays BO properties via bindings
-   [[list-objects]] — UI renders LO items in lists via dataSource binding
-   [[cockpit-cards]] — Cards use CardContainer + CockpitList + lazy loading
-   [[business-logic]] — BL methods return visibility/editability booleans
-   [[calendar-control]] — Native calendar with drag-and-drop, reschedule, sidebar planning

## Validation rules

UIDescription is by far the largest contract the modeler validates — the single-file validator is 4,444 lines covering roughly 200 distinct rule codes. The rules below are the ones authors hit most often; the per-rule catalogue lives in the primary repo.

### Cross-cutting (every contract)

-   Contract names must be unique workspace-wide — rename a colliding UIDescription.
-   Files must be readable, well-formed XML with `<UIDescription>` as the root.

### Must

-   Root element is `<UIDescription>`.
-   File name ends with `.uidescription.xml`. Custom UIDescriptions start with the customizing indicator in both the file name and the root `@name`.
-   A `FastDataEntryGrid` must live inside a `SingleElementArea` which is itself inside a `SingleAreaSection` — other parents are rejected.
-   A `CardContainer` / `SyncCardContainer` / `QuickActionCardContainer` must declare `isReadyToLoad`, `loadContainerData`, `onAutoReload`, `onDateChange`, and `isCollapsible` correctly — roughly 15 narrow rules govern card-container bindings.
-   `DataSearchField` parents and binding targets follow a fixed shape — the validator rejects deviations.
-   Area patterns (ButtonGrid, Card, Login, SingleElement, Welcome, FilterElement) each have specific parent/child rules — putting the wrong element inside an area is rejected.
-   Every `UIPluginV2` reference must resolve to a real UIPluginV2 contract; each binding `target` must match a property id declared on that plugin; list `target` must match the plugin's list id; list binding `target` must match the plugin's list property id; and a `CustomPluginEvent @name` must match a declared event name on the plugin.
-   A binding `groupBy` / `badgeText` / `longPressMenu` / flex / itemAnimation rule set applies — each has its own small error code but all are "use the documented shape."

### Must not

-   `UIPluginV2 @name` must not be duplicated within a single UIDescription, and a `CustomPluginEvent @name` must not repeat on the same plugin.
-   Legacy `<UIPlugin>` (v1) elements are deprecated — any usage is warned; migrate to v2.

### Coerced (silently rewritten)

-   `pagePattern` values are case-normalized to `SingleSectionPage` / `SingleSectionDialogPage` / `MasterDetailSectionPage` / `MultiSectionPage` / `SplitScreenPage`. Authoring `singlesectionpage` silently rewrites to `SingleSectionPage`.
-   `bindingMode` values are case-normalized to `ONE_WAY` / `TWO_WAY` / `ONE_TIME`.
-   `xmlns="*.xsd"` on the root is stripped.

Internal schema reference: `rcg-mobile-dev-agent/wiki/contracts/ui-description.md`.
