Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Dated delivery history and release records. Each release is a `## vX.Y.Z — YYY

## Unreleased

- Added a controlled public [`authoring::ColorPicker`](docs/guides/color-picker.md) for opaque colors. Core `HsvColor` keeps hue, saturation and value of the sRGB components, with `to_color` and `format_hex_color`. The picker returns an inline trigger with a swatch and `#RRGGBB` value and a root-sibling panel holding a saturation/value area and a hue strip, drawn with ordinary gradients and input-transparent thumbs. The additive `color_picker` role, named by its label with the hex `value` and `expanded` state, joins UI JSON v1, semantics, Semantic JSON v1, Replay v1/v2 and inspection/invariant targets; the panel is a `group`. `ColorPickerDispatcher` opens at the area, captures primary presses on either control, maps arrows, Home/End and gamepad directions to configurable steps, rounds requested values, moves between the controls with Tab and dismisses like ComboBox, plus Enter. The [Inspector](docs/guides/inspector-hierarchy.md#display-color-picker) gains a per-item Display color on the Display tab, restored by Reset and checked by window-free keyboard/pointer and injected native smokes. Hex or channel entry, alpha, revert and numeric range semantics remain out of scope.

- Prepare each Replay generation's first semantic observation before publishing either output.

- Share Replay's focus reveal scroll updates and finish layout before refreshing pointers and publishing the generation.
Expand Down
1 change: 1 addition & 0 deletions cmake/TesseraCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_library(tessera_core
src/ui/component_mount.cpp
src/ui/component_tree_mount.cpp
src/ui/async.cpp
src/style/color.cpp
src/style/resolved_style.cpp
src/style/style_sheet.cpp
src/style/theme.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/TesseraTesting.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tessera_add_contract_test(combobox tests/input/combobox_tests.cpp)
tessera_add_contract_test(toggle tests/input/toggle_tests.cpp)
tessera_add_contract_test(edit_input tests/input/edit_input_tests.cpp)
tessera_add_contract_test(vector_input tests/input/vector_input_tests.cpp)
tessera_add_contract_test(color_picker tests/input/color_picker_tests.cpp)
if(TESSERA_BUILD_EXAMPLES)
tessera_add_check(first_app tessera_first_app smoke)
tessera_add_check(settings_starter tessera_settings_starter smoke)
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Update the page whose contract, procedure or status changes; link to it elsewher

## Guides

[Getting Started](guides/getting-started.md) · [First app](guides/first-app.md) · [Settings starter](guides/settings-starter.md) · [Hierarchy / Inspector](guides/inspector-hierarchy.md) · [SplitPane](guides/split-pane.md) · [Scrollbar](guides/scrollbar.md) · [Stack](guides/stack.md) · [Grid](guides/grid.md) · [Popup/Tooltip](guides/popup-tooltip.md) · [Modal/Dialog](guides/modal-dialog.md) · [TreeView/ListView](guides/tree-list.md) · [Menu/ContextMenu](guides/menu-context.md) · [RadioGroup](guides/radio-group.md) · [Tabs/Toolbar](guides/tabs-toolbar.md) · [ComboBox](guides/combobox.md) · [Checkbox/Switch](guides/checkbox-switch.md) · [TextInput/NumberInput](guides/text-number-input.md) · [VectorInput](guides/vector-input.md) · [Renderer telemetry](guides/renderer-diagnostics.md) · [Development](guides/development.md) · [Testing](guides/testing.md)
[Getting Started](guides/getting-started.md) · [First app](guides/first-app.md) · [Settings starter](guides/settings-starter.md) · [Hierarchy / Inspector](guides/inspector-hierarchy.md) · [SplitPane](guides/split-pane.md) · [Scrollbar](guides/scrollbar.md) · [Stack](guides/stack.md) · [Grid](guides/grid.md) · [Popup/Tooltip](guides/popup-tooltip.md) · [Modal/Dialog](guides/modal-dialog.md) · [TreeView/ListView](guides/tree-list.md) · [Menu/ContextMenu](guides/menu-context.md) · [RadioGroup](guides/radio-group.md) · [Tabs/Toolbar](guides/tabs-toolbar.md) · [ComboBox](guides/combobox.md) · [ColorPicker](guides/color-picker.md) · [Checkbox/Switch](guides/checkbox-switch.md) · [TextInput/NumberInput](guides/text-number-input.md) · [VectorInput](guides/vector-input.md) · [Renderer telemetry](guides/renderer-diagnostics.md) · [Development](guides/development.md) · [Testing](guides/testing.md)

## Product and consumers

Expand Down
2 changes: 2 additions & 0 deletions docs/architecture/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ A focused `tab` follows the same roving rule within its nearest `tab_list`, but

A focused `option` uses the list rule within its nearest `listbox`: Up/Down move through eligible options in preorder and stop at either end, unmodified Home/End reach the first/last, Left/Right do nothing and movement never selects; `Activate` requests the focused option's action. `ComboBoxDispatcher`, constructed with the trigger ID, routes the `<id>-list` popup before ordinary input: opening requests the selected eligible option's focus, else the first eligible one; non-repeating Alt+Down on the focused closed trigger returns `open`; Escape, `Cancel`, Alt+Up and outside presses (including the trigger) return consumed dismissal with trigger focus; sequential focus commands return unconsumed dismissal; and hover requests an option's focus. Refresh dismisses a list whose trigger disappears or becomes ineligible. The [ComboBox guide](../guides/combobox.md) and [owner tests](../../tests/input/combobox_tests.cpp) own this boundary.

`ColorPickerDispatcher`, constructed with the trigger ID, routes the `<id>-panel` popup before ordinary input with the host's accepted `HsvColor`. Opening requests the area's focus; Alt+Down on the focused closed trigger returns `open`. A primary press on the area or hue strip focuses it and captures that pointer until release or `PointerCancel`; the press, captured moves and release request the clamped color under the pointer over the control's content box. The focused area maps Left/Right to saturation and Up/Down to value, and the strip maps Left/Down and Right/Up to hue with Home/End selecting its ends, all by configured steps. Each changed color is returned with the control's activate request for the host to execute; unchanged values are consumed without one. Tab moves between the two controls and past either end returns unconsumed dismissal; Enter, `Activate`, Escape, `Cancel`, Alt+Up and outside presses return consumed dismissal with trigger focus. The [ColorPicker guide](../guides/color-picker.md) and [owner tests](../../tests/input/color_picker_tests.cpp) own this boundary.

`tree_parent` preserves logical ancestry for flat layout rows. Disabled logical ancestors participate in shared input eligibility and `:disabled` styling. If a surviving focused tree item becomes hidden/disabled, reconciliation first tries its nearest eligible logical ancestor, then ordinary neighbor recovery. This also handles author-ID replacement after controlled collapse. Removed keys use ordinary neighbor recovery. The [public guide](../guides/tree-list.md) owns controlled state, scrolling and command wiring; [owner tests](../../tests/input/collection_tests.cpp) cover navigation, state, rejection and codec boundaries.

Map gamepad input into logical navigation/activation/cancel actions at the host boundary. Dead zones and repeat policy must have an explicit owner, as in the [Win32 host](#host-boundary). Do not put device-specific polling in buttons.
Expand Down
8 changes: 5 additions & 3 deletions docs/architecture/semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ The [Modal](input.md#implemented-modal-interaction) marker takes role precedence

[VectorInput](../guides/vector-input.md) projects the additive `group` role from `collection_role: group`. The group is named by its label reference and exposes no action, value or selection; each component under it is an ordinary `number_input` named by its own label. The role round trips in Semantic JSON v1 and Replay v1/v2 targets and is accepted by inspection/invariants; older readers reject it.

[ColorPicker](../guides/color-picker.md) projects the additive `color_picker` role from `collection_role: color_picker`. The trigger is named by its display-none title and carries `value`, the `#RRGGBB` text taken from its Box `edit_value`, and `expanded`, the controlled open state, with one ordinary activate action while enabled; its swatch and Text are presentational. The open panel is a `group` named by its display-none title; its saturation/value area and hue strip are buttons named by labels stating their values, without numeric range semantics. A closed panel has no entries. The role round trips in Semantic JSON v1 and Replay v1/v2 targets and is accepted by inspection/invariants; older readers reject it.

[Checkbox and Switch](../guides/checkbox-switch.md) project additive `checkbox`/`switch` roles from the Box-only `toggle_role`. Each is named by its label reference and carries `selected` as its checked/on state and, while enabled, one ordinary activate action; marker, track and thumb are presentational. Both roles round trip in Semantic JSON v1 and Replay v1/v2 targets and are accepted by inspection/invariants; older readers reject them. No mixed state or native accessibility adapter is introduced.

[TextInput and NumberInput](../guides/text-number-input.md) project additive `text_input`/`number_input` roles from the Box-only `edit_role`. Each is named by its label reference and carries `value`, the presented text taken from the paired Box-only `edit_value` (the draft text for a number input, not a parsed number), and while enabled one ordinary activate action. Label, value, selection and step Text are presentational. Both roles and `value` round trip in Semantic JSON v1 and Replay v1/v2 and are accepted by inspection/invariants; older readers reject them. Selection, caret geometry, numeric range and increment/decrement actions are not exposed, and no native editable-text adapter is introduced.
Expand All @@ -68,12 +70,12 @@ Semantic JSON v1 requires the original fields, permits optional collection state
| Object | Fields |
| --- | --- |
| Snapshot | `version`: 1; `generation`: canonical unsigned 64-bit decimal **string**; `node_count`: total authored nodes, including flattened/hidden/display-none nodes; `nodes`: ordered semantic entries |
| Entry | `node`: authored preorder index; `id`: author ID or null; `parent`: semantic entry index or null; `role`: `root`, `button`, `text`, `separator`, `dialog`, `list`, `tree`, `list_item`, `tree_item`, `menu`, `menu_item`, `scrollbar`, `radio_group`, `radio`, `checkbox`, `switch`, `text_input`, `number_input`, `tab_list`, `tab`, `toolbar`, `combobox`, `listbox`, `option`, or `group`; `name`: UTF-8 string; `name_source`: `none`, `relationship`, or `content`; `labelled_by`: authored preorder index or null; `enabled`, `focusable`, `focused`: booleans; `actions`: eligible action array; optional `selected` and `expanded`: boolean or null; optional `value`: UTF-8 string |
| Entry | `node`: authored preorder index; `id`: author ID or null; `parent`: semantic entry index or null; `role`: `root`, `button`, `text`, `separator`, `dialog`, `list`, `tree`, `list_item`, `tree_item`, `menu`, `menu_item`, `scrollbar`, `radio_group`, `radio`, `checkbox`, `switch`, `text_input`, `number_input`, `tab_list`, `tab`, `toolbar`, `combobox`, `listbox`, `option`, `group`, or `color_picker`; `name`: UTF-8 string; `name_source`: `none`, `relationship`, or `content`; `labelled_by`: authored preorder index or null; `enabled`, `focusable`, `focused`: booleans; `actions`: eligible action array; optional `selected` and `expanded`: boolean or null; optional `value`: UTF-8 string |
| Action | `binding`: `activate` or the applicable tree `expand`/`collapse`; `action`: host action name |

No handles, tree identities, pointers, or platform types are encoded. Geometry is joined by authored node index from the same generation under [inspection](inspection.md), rather than duplicating layout fields. Collection state is additive: canonical output omits absent state fields, preserving legacy record bytes. Selection belongs only to list/tree items, radios, tabs, options and toggles, expansion only to tree items and comboboxes (required on a combobox), and `value` exactly to text inputs, number inputs and comboboxes. Older readers reject new roles/state/actions rather than silently treating them as buttons. No other role carries a value. Relationship references can name nodes with no semantic entry and never request transitive name resolution.
No handles, tree identities, pointers, or platform types are encoded. Geometry is joined by authored node index from the same generation under [inspection](inspection.md), rather than duplicating layout fields. Collection state is additive: canonical output omits absent state fields, preserving legacy record bytes. Selection belongs only to list/tree items, radios, tabs, options and toggles, expansion only to tree items, comboboxes and color pickers (required on the latter two), and `value` exactly to text inputs, number inputs, comboboxes and color pickers. Older readers reject new roles/state/actions rather than silently treating them as buttons. No other role carries a value. Relationship references can name nodes with no semantic entry and never request transitive name resolution.

Validation bounds the authored count to 1–10000 and entries to that count, requires strictly increasing authored indices, unique author IDs, and preceding parents with contiguous semantic subtrees. References stay within the authored count. A `root` role names only the authored root. Names have source `none` exactly when empty; a relationship-derived name requires a reference. Disabled entries cannot be focusable or expose actions; focus requires focusability and at most one focused entry. Actions belong only to enabled buttons, separators, scrollbars, items, radios, tabs, options, toggles, inputs or comboboxes, contain at most one `activate` plus one applicable tree expansion action without duplicate bindings, and use nonempty UTF-8 host names without ASCII controls. Empty forests are valid, for example for entirely invisible content.
Validation bounds the authored count to 1–10000 and entries to that count, requires strictly increasing authored indices, unique author IDs, and preceding parents with contiguous semantic subtrees. References stay within the authored count. A `root` role names only the authored root. Names have source `none` exactly when empty; a relationship-derived name requires a reference. Disabled entries cannot be focusable or expose actions; focus requires focusability and at most one focused entry. Actions belong only to enabled buttons, separators, scrollbars, items, radios, tabs, options, toggles, inputs, comboboxes or color pickers, contain at most one `activate` plus one applicable tree expansion action without duplicate bindings, and use nonempty UTF-8 host names without ASCII controls. Empty forests are valid, for example for entirely invisible content.

SplitPane's separator name includes the host-controlled logical size and its activate binding requests the ordinary adjustment command. Numeric value/range/orientation fields, separate increment/decrement actions and native accessibility adapters remain unimplemented. `separator` is an additive role in semantic JSON v1 and replay role/name targets; previous builds reject the new role rather than silently treating it as a button. Existing root/button/text records keep their encoding and behavior. [SplitPane checks](../../tests/input/split_pane_tests.cpp) cover marker and semantic round trips, replay role resolution/invocation, disabled eligibility and generation rejection.

Expand Down
Loading