synit-manual/src/protocols/synit/ui.md

201 lines
8.8 KiB
Markdown

# User interface definitions and interaction
- [`[synit]/protocols/schemas/ui.prs`](https://git.syndicate-lang.org/synit/synit/src/branch/main/protocols/schemas/ui.prs)
The user interface protocol is perhaps the most subject-to-change part of the whole system. It
is a client-server protocol, similar in spirit to X-Windows, where *clients* request display
and input services from a *server*, to which is attached a display and input devices.
At present, it is a simple system with a fixed set of widget types, a TeX-inspired box-and-glue
layout model, and a very limited set of event types. In future, a
NeWS/Display-PostScript-inspired model could dovetail very nicely with the capability and
dataspace features of Syndicate.
**Implementation.** The SqueakPhone Smalltalk image includes the initial implementation of the
protocol, in classes `WidgetDaemon`, `WidgetBuilder`, `WidgetWindow`, and so on.
## Creating a window
A client *observes* `Window` assertions with an `id` of its choice. The server notices the
client's interest, and in response, creates a fresh dataspace for configuration and interaction
relating to the new window, and asserts a `Window` record mapping the `id` to the new `space`.
```
Window = <window @id WidgetId @space #!any> .
WidgetId = any .
```
## Configuring a window
Within the dataspace referred to by a `Window` assertion—henceforth the *window dataspace*—the
client may assert `WindowCloseable` to add a close button to the window decoration, and may
assert `WindowTitle` to give the window a name.
```
WindowCloseable = <window-closeable> .
WindowTitle = <window-title @title string> .
```
## Creating widget trees
The client may place `Widget` assertions within the window dataspace to create new widgets
within the window. The window is hidden until the first `Widget` is asserted.
`Root` and `Parent` assertions connect new widgets to the overall window layout tree. A `Root`
assertion places the widget directly in the containing window, while a `Parent` assertion marks
a widget as child of another widget. In both cases, the `order` sort key is used when multiple
children are present within a container that supports widget ordering.
```
Widget = <widget @id WidgetId @type WidgetType> .
Parent = <parent @id WidgetId @parentId WidgetId @order SortKey> .
Root = <root @id WidgetId @order SortKey> .
SortKey = @double double / @string string .
```
## Widget Types
Widgets acting as containers for other widgets may be of either `column` or `row` type. Leaf
widgets may be `blank` (for spacing/padding/layout), `text` (a label or editable field), a
`slider`, or a [FontAwesome](https://fontawesome.com/) `icon`.
```
WidgetType = NodeType / LeafType .
NodeType = =column / =row .
LeafType = =blank / =text / =slider / =icon .
```
## Configuring widgets
Widgets have *attributes* attached to them. An attribute is a pair of a symbol `key` and a
`value` (of `key`-specific type) attached to a particular widget. Most attribute keys are
expected to have either zero or one `Attribute` records for any given widget, but the
Syndicated Actor Model naturally supports *multiple* values for a given attribute, and some
attribute keys take advantage of this. See [below](#attribute-keys) for more on the available
attribute keys.
```
Attribute = <attribute @id WidgetId @key symbol @value any> .
```
## Events and Widget State
Widgets marked with the [`interactive` attribute](#interactive) generate events in response to
user interaction.
Clients can observe `Touch` assertions to receive information about when the user has a finger
touching the displayed widget on a touchscreen. The assertion for a given widget will appear
when the touch starts, and disappear when the touch ends. Multiple touches, uniquely
identified, may be active simultaneously.
```
Touch = <touch @widget WidgetId @touchId any> .
```
Clients can observe `Click` messages to receive information about when the user removes a
touching finger from a widget while the finger is within the widget's bounds.
```
Click = <click @widget WidgetId> .
```
Finally, whether a widget is marked interactive or not, the UI server actor asserts `State`
assertions containing facts about a given widget's state. For example, a text widget asserts a
`State` assertion with the symbol `text` as its `key` and a string as its `value`; a slider
asserts a `value`-keyed `State`; and a scrollable widget asserts a `visible-scroll-range`-keyed
`State` with a `VisibleScrollRange` value.
```
State = <state @widget WidgetId @key any @value any> .
VisibleScrollRange =
/ =none
/ @visibleScrollRange <visible-scroll-range
<min @minId WidgetId @minSortKey SortKey>
<max @maxId WidgetId @maxSortKey SortKey>>
.
```
## Accessing widget instances
Within the current implementation, access to the raw
[Morphic](http://wiki.squeak.org/squeak/morphic) object representing the widget can be gained
by monitoring `WidgetInstance` assertions. (This is not a sustainable technique, and it will be
replaced in future by an entity-reference-based system.)
```
WidgetInstance = <widget-instance @id WidgetId @instance #!any> .
```
## <span id="attribute-keys"></span>Widget attributes
### General attributes, for any widget type
| Key | Value type | Description |
|-----------------|-----------------------|-----------------------------------------------------------------------------------|
| padding | `BoxSize` | Layout: padding |
| spacing | `BoxSize` | Layout: spacing |
| size | `BoxSize` | Layout: explicit widget size |
| backgroundColor | `Color` | The background color of the widget |
| foregroundColor | `Color` | Text color in a label or editable field; icon color for FontAwesome icons |
| cornerStyle | `square` or `rounded` | The widget's corner style. Defaults to `square` |
| cornerRadius | number | The widget's corner radius (where `cornerStyle` is `rounded`), measured in points |
| interactive | boolean | If true, enables touch and click events |
| name | string | Sets the Morphic "name" for the widget |
### Icon attributes
| Key | Value type | Description |
|------------|------------|-------------------------------------------|
| icon | symbol | The FontAwesome icon name for icons |
| icon-style | symbol | The FontAwesome icon style name for icons |
### Slider attributes
| Key | Value type | Description |
|-------------|----------------------------|---------------|
| max | number | Maximum value |
| min | number | Minimum value |
| value | number | Initial value |
| orientation | `vertical` or `horizontal` | Orientation |
### Text attributes
| Key | Value type | Description |
|----------|------------|--------------------------------------------------------------|
| fontSize | number | The font size, measured in points |
| readOnly | boolean | If true or absent, a label; if false, an editable text field |
| value | string | Initial value |
### Row and column attributes
| Key | Value type | Description |
|------------|------------|------------------------------------------------------------------------------|
| cells | integer | Number of cells per row (column) in a grid; if absent, just one row (column) |
| scrollable | boolean | Whether the container is a scrollable viewport or fixed-size |
## Widget value types
### Color values
The `Color` type describes an RGBA color value where the components are `double`s in the range
`0.0` to `1.0` (inclusive).
```
Color = <rgba @red double @green double @blue double @alpha double> .
```
### `BoxSize`: layout sizes
The `BoxSize` type is a pair of `Sizing`s, one for the horizontal and one for the vertical
dimension. Each `Sizing` describes an *ideal* size, measured in points, plus a "stretch" and a
"shrink" specification of `Fill` type, loosely modelled on the TeX concept of "boxes and glue".
```
Fill = @fixed double / <fill @weight int @rank int> .
Sizing = <sizing @ideal double @stretch Fill @shrink Fill> .
BoxSize = <box-size @horizontal Sizing @vertical Sizing> .
```