morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
view.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4#include <algorithm>
5#include <concepts>
6#include <cstddef>
7#include <cstdint>
8#include <functional>
9#include <memory>
10#include <optional>
11#include <stdexcept>
12#include <string>
13#include <type_traits>
14#include <utility>
15#include <variant>
16#include <vector>
17
20#include "../util/datetime.hpp"
21#include "alignment.hpp"
22#include "button_emphasis.hpp"
23#include "button_style.hpp"
24
25/// @file
26/// @brief The toolkit-agnostic view tree: props, node kinds and their builders.
27///
28/// Specified in `docs/spec/ui/view_tree.md`.
29
30namespace morph::ui {
31
32/// @brief A widget event handler taking no arguments.
33using Action = std::function<void()>;
34
35/// @brief The identity of a Switch case, a Select option, a ForEach or Table row and a drag payload.
36///
37/// Never a floating-point number: morph ids can exceed 2^53.
38using Key = std::variant<std::int64_t, std::string>;
39
40/// @brief The stable identity of a slot: a bound property a renderer can refer to by number.
41///
42/// Whoever builds the tree assigns slot ids; the interpreter numbers every bound property of a document, and a
43/// code-generating renderer names the value it reads after it (`v.s17`). `Mounted` does not use them. A slot made
44/// from a bare callable has no id.
45using SlotId = std::uint32_t;
46
47/// @brief A slot's read together with its id, as `ui::slot` makes it; a `Prop` made from it is a slot with that id.
48/// @tparam F A callable taking no arguments.
49template <typename F>
51 /// @brief The slot's id.
52 SlotId id = 0;
53 /// @brief Reads the slot's value; every signal it reads is a dependency.
55};
56
57/// @brief A slot with an id.
58/// @tparam F A callable taking no arguments.
59/// @param slotId The slot's id.
60/// @param read Reads the value; every signal it reads is a dependency.
61/// @return What a `Prop` takes as a slot with @p slotId.
62template <typename F>
63[[nodiscard]] SlotBinding<std::decay_t<F>> slot(SlotId slotId, F&& read) {
64 return SlotBinding<std::decay_t<F>>{.id = slotId, .read = std::forward<F>(read)};
65}
66
67namespace detail {
68
69/// @brief Whether a type is a `SlotBinding`: false here, true for the specialisation below.
70/// @tparam T The type.
71template <typename T>
72struct IsSlotBinding : std::false_type {};
73
74/// @brief A `SlotBinding` is one.
75/// @tparam F The read's type.
76template <typename F>
77struct IsSlotBinding<SlotBinding<F>> : std::true_type {};
78
79/// @brief Whether `From` may become a constant `Prop<To>`: an implicit conversion that does not silently change
80/// the kind of value.
81///
82/// Two implicit conversions are refused because they compile and then show something else. A pointer, a string
83/// literal included, converts to `bool` as "not null", so `Prop<bool>{"false"}` would be `true`. A floating-point
84/// value converts to an integral type by truncation, so `Prop<std::int64_t>{2.9}` would be `2`. Other narrowing,
85/// such as `int` to `std::size_t`, is kept: `Prop<std::size_t> count = 0` is the ordinary way to write a count.
86///
87/// `From` is the source type as deduced for a forwarding reference; `To` is the property's value type.
88template <typename From, typename To>
89concept ConstantConvertible = std::convertible_to<From, To> &&
90 !(std::is_same_v<std::remove_cv_t<To>, bool> && std::is_pointer_v<std::decay_t<From>>) &&
91 !(std::is_integral_v<To> && std::is_floating_point_v<std::remove_cvref_t<From>>) &&
93
94} // namespace detail
95
96/// @brief A node property: a constant, or a slot — a reactive read, with a stable id when it was given one.
97///
98/// A constant is set on the widget once and creates no reactive node. A slot becomes an equality-gated
99/// `reactive::Computed` plus an `reactive::Effect` calling one widget setter. An input whose value is a slot is
100/// controlled: after a user's edit, the widget shows what the slot says. A constant is not re-asserted.
101///
102/// A braced list cannot initialise a `Prop`, because the constant constructor deduces its argument's type: name
103/// the type, as in `.options = std::vector<ui::SelectOption>{{...}, {...}}`.
104/// @tparam T The property's value type.
105template <typename T>
106class Prop {
107public:
108 /// @brief The value-initialised constant.
110 requires std::default_initializable<T>
111 : _value{std::in_place_index<0>} {}
112
113 /// @brief A constant.
114 ///
115 /// A callable is never a constant, even when it converts to `T`: a captureless lambda converts to a function
116 /// pointer and that to `bool`, so `Prop<bool>{[] { return false; }}` would otherwise be the constant `true`.
117 ///
118 /// A pointer does not become a `bool` constant and a floating-point value does not become an integral one; see
119 /// `detail::ConstantConvertible`.
120 /// @tparam U A non-callable type that converts to `T` without changing the kind of value.
121 /// @param value The constant.
122 template <typename U>
123 requires(!std::invocable<U&>) && detail::ConstantConvertible<U, T>
124 Prop(U&& value)
125 // A string literal converting to `T` decays here; that is the conversion.
126 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
127 : _value{std::in_place_index<0>, static_cast<T>(std::forward<U>(value))} {}
128
129 /// @brief A slot without an id.
130 ///
131 /// The read must be callable: an empty `std::function` or a null function pointer is refused here, where the
132 /// mistake is made, rather than when a mount first calls it.
133 /// @tparam F A callable taking no arguments and returning something convertible to `T`.
134 /// @param binding Evaluated by the mount inside a `Computed`; every signal it reads is a dependency.
135 /// @throws std::invalid_argument when `binding` is an empty `std::function` or a null function pointer.
136 template <typename F>
137 requires std::invocable<F&> && std::convertible_to<std::invoke_result_t<F&>, T>
138 Prop(F binding) : _value{std::in_place_index<1>, Slot{.id = std::nullopt, .read = std::move(binding)}} {
139 refuseEmpty();
140 }
141
142 /// @brief A slot with an id, as `ui::slot` makes it.
143 /// @tparam F A callable taking no arguments and returning something convertible to `T`.
144 /// @param binding The id and the read.
145 /// @throws std::invalid_argument when the read is an empty `std::function` or a null function pointer.
146 template <typename F>
147 requires std::invocable<F&> && std::convertible_to<std::invoke_result_t<F&>, T>
149 : _value{std::in_place_index<1>, Slot{.id = binding.id, .read = std::move(binding.read)}} {
150 refuseEmpty();
151 }
152
153 /// @brief Whether this is a slot.
154 /// @return True for a slot, false for a constant.
155 [[nodiscard]] bool isBound() const noexcept { return _value.index() == 1; }
156
157 /// @brief The constant. Only valid when `!isBound()`.
158 /// @return The constant.
159 /// @throws std::bad_variant_access when this is a slot.
160 [[nodiscard]] T const& constant() const { return std::get<0>(_value); }
161
162 /// @brief The slot's read. Only valid when `isBound()`.
163 /// @return The read.
164 /// @throws std::bad_variant_access when this is a constant.
165 [[nodiscard]] std::function<T()> const& binding() const { return std::get<1>(_value).read; }
166
167 /// @brief The slot's id.
168 /// @return The id given through `ui::slot`; `nullopt` for a constant and for a slot made from a bare callable.
169 [[nodiscard]] std::optional<SlotId> slotId() const noexcept {
170 Slot const* const bound = std::get_if<1>(&_value);
171 return bound == nullptr ? std::nullopt : bound->id;
172 }
173
174 /// @brief The current value: the constant, or the slot read once.
175 /// @return The value.
176 [[nodiscard]] T evaluate() const { return isBound() ? std::get<1>(_value).read() : std::get<0>(_value); }
177
178private:
179 /// @brief A slot: its id, if it has one, and its read.
180 struct Slot {
181 /// @brief The id, or none.
182 std::optional<SlotId> id;
183 /// @brief The read; never empty.
184 std::function<T()> read;
185 };
186
187 /// @brief Refuses a slot whose read is empty: an empty `std::function` or a null function pointer.
188 /// @throws std::invalid_argument when the read is empty.
189 void refuseEmpty() const {
190 if (!std::get<1>(_value).read) {
191 throw std::invalid_argument{"ui::Prop: a binding must not be empty"};
192 }
193 }
194
195 std::variant<T, Slot> _value;
196};
197
198/// @brief How a `Text` is styled; each backend maps a role to its theme.
199enum class TextRole : std::uint8_t {
200 Normal, ///< Body text.
201 Muted, ///< Secondary text.
202 Heading, ///< A section heading.
203 Error, ///< An error message.
204 Success, ///< A confirmation.
205 Section, ///< The heading of a group within a list: body size, bold and muted, below a `Heading`.
206};
207
208/// @brief How a `TextInput` edits.
209enum class TextInputMode : std::uint8_t {
210 SingleLine, ///< One line; Enter submits.
211 Multiline, ///< Several lines.
212 Password, ///< One line, masked.
213};
214
215/// @brief How a `Select` presents its options.
216enum class SelectStyle : std::uint8_t {
217 Dropdown, ///< A closed field that opens a list.
218 Radio, ///< Every option visible, one marked.
219};
220
221/// @brief The direction a stack arranges, or a scroll area scrolls, its content.
222enum class Axis : std::uint8_t {
223 Vertical, ///< Top to bottom.
224 Horizontal, ///< Left to right.
225};
226
227/// @brief What a `DateTimeInput` edits.
228enum class DateMode : std::uint8_t {
229 Date, ///< A calendar date; the time of day is midnight in the display zone.
230 DateTime, ///< A date and a time of day.
231};
232
233/// @brief Whether a `FilePicker` chooses an existing file or a file to write.
234enum class FilePickerMode : std::uint8_t {
235 Open, ///< An existing file.
236 Save, ///< A file to create or overwrite.
237};
238
239/// @brief What a message or a mark conveys, mapped to the theme's colours.
240enum class Tone : std::uint8_t {
241 Neutral, ///< Nothing in particular.
242 Info, ///< Information.
243 Ok, ///< Success.
244 Warn, ///< Something to check.
245 Err, ///< A failure.
246};
247
248/// @brief The edge a `Drawer` slides in from, in reading direction.
249enum class Side : std::uint8_t {
250 Start, ///< The left edge in a left-to-right layout.
251 End, ///< The right edge in a left-to-right layout.
252};
253
254/// @brief Where one step of a `Steps` indicator stands.
255enum class StepState : std::uint8_t {
256 Pending, ///< Not started.
257 Active, ///< In progress.
258 Done, ///< Completed.
259 Failed, ///< Stopped by a failure.
260};
261
262/// @brief How many rows of a `Table` the user can select.
263enum class SelectionMode : std::uint8_t {
264 None, ///< Rows can be activated, not selected.
265 Single, ///< At most one row.
266 Multiple, ///< Any number of rows.
267};
268
269/// @brief How much space a widget asks for along one dimension, in backend units (a character cell on the TUI).
270struct Sizing {
271 /// @brief The sizing rule.
272 enum class Kind : std::uint8_t {
273 Content, ///< As much as the content needs.
274 Fixed, ///< Exactly `amount` units.
275 Stretch, ///< A share of the leftover space, weighted by `amount`.
276 };
277 /// @brief The rule.
279 /// @brief Units for `Fixed`, weight for `Stretch`, unused for `Content`.
280 int amount = 0;
281
282 /// @brief Size to the content.
283 /// @return The sizing.
284 [[nodiscard]] static constexpr Sizing content() noexcept { return {}; }
285
286 /// @brief A fixed size.
287 /// @param units The size in backend units.
288 /// @return The sizing.
289 [[nodiscard]] static constexpr Sizing fixed(int units) noexcept { return {.kind = Kind::Fixed, .amount = units}; }
290
291 /// @brief A share of the leftover space.
292 /// @param weight The share's weight.
293 /// @return The sizing.
294 [[nodiscard]] static constexpr Sizing stretch(int weight = 1) noexcept {
295 return {.kind = Kind::Stretch, .amount = weight};
296 }
297
298 /// @brief Memberwise equality.
299 /// @return Whether both rules and amounts match.
300 bool operator==(Sizing const&) const = default;
301};
302
303/// @brief A widget's size request in both dimensions, and the bounds its extent is clamped into.
304///
305/// A bound clamps the extent the sizing gives, whatever the sizing: a stretch child's share stops at its maximum
306/// and a content or fixed child takes at least its minimum. A negative bound, a minimum above its maximum and a
307/// fixed amount outside its bounds are refused at mount (`std::logic_error`).
309 /// @brief Along the horizontal axis.
311 /// @brief Along the vertical axis.
313 /// @brief The least width, in backend units; `nullopt` for none.
314 std::optional<int> minWidth;
315 /// @brief The most width, in backend units; `nullopt` for none.
316 std::optional<int> maxWidth;
317 /// @brief The least height, in backend units; `nullopt` for none.
318 std::optional<int> minHeight;
319 /// @brief The most height, in backend units; `nullopt` for none.
320 std::optional<int> maxHeight;
321 /// @brief Where the node sits across its row's, column's or grid row's axis; `nullopt` takes the container's
322 /// `align`.
323 std::optional<Align> alignSelf;
324
325 /// @brief Memberwise equality.
326 /// @return Whether the sizings, the bounds and the alignment match.
327 bool operator==(LayoutHints const&) const = default;
328};
329
330/// @brief What assistive technology announces for a node.
332 /// @brief The accessible name; empty leaves the widget's own text as its name.
334 /// @brief The role, such as `"heading"`; empty leaves the kind's own role. Set once.
335 std::string role;
336};
337
338/// @brief A keyboard chord a node handles while focus is inside it.
340 /// @brief The chord: modifiers (`Ctrl`, `Shift`, `Alt`, `Meta`) joined to a key name, such as `"Ctrl+S"`.
341 std::string chord;
342 /// @brief Called when the chord is pressed.
344};
345
346/// @brief The part every node carries: visibility, enablement, layout, drag-and-drop, accessibility, test identity,
347/// tooltip, surface, keyboard chords and autofocus.
348struct Common {
349 /// @brief Whether the widget is shown.
351 /// @brief Whether the widget accepts input.
353 /// @brief The size request; set once.
355 /// @brief Engaged: the widget can be dragged, and a drop delivers this key.
357 /// @brief Whether a drop of a key is accepted; empty accepts every key. Used only when `onDrop` is set.
358 std::function<bool(Key const&)> accepts;
359 /// @brief Set: the widget is a drop target, called with the dropped key.
360 std::function<void(Key)> onDrop;
361 /// @brief The accessible name and role.
363 /// @brief The stable identifier an automated test finds the widget by; empty means none. Set once.
364 std::string testId;
365 /// @brief The name a focus request after mount finds the widget by (`Mounted::focus`), such as a document
366 /// element's id; empty means none. Set once.
367 std::string focusId;
368 /// @brief Text shown while the pointer rests on the widget; empty shows none.
370 /// @brief The named token set of the theme that styles this node and everything inside it; empty is the
371 /// theme's default. Set once.
372 std::string surface;
373 /// @brief Chords handled while focus is inside the node; the innermost node declaring a chord takes it. For a
374 /// chord listed twice, the first entry is the one called.
375 std::vector<KeyBinding> keys;
376 /// @brief Whether the widget takes keyboard focus when the content it is part of mounts; the first such widget
377 /// in document order wins.
378 bool autofocus = false;
379};
380
381/// @brief The state of a value a user edits or reads: whether it may be changed, must be given, is wrong, or is
382/// being recomputed.
383///
384/// On text, which no user edits, `readonly` changes nothing; the other three mark it as they mark an input. A
385/// constant default calls no setter.
387 /// @brief Whether the user may not change the value. A read-only field shows its value and can take focus and
388 /// have its text selected; it takes no edit, submit or commit.
390 /// @brief Whether the field is marked as one that must be given. Only a mark: whether a value is missing is the
391 /// application's to decide and show through `errors`.
393 /// @brief The messages shown with the field, in order; empty shows none. Which errors show, and when, is
394 /// decided before they reach this property.
396 /// @brief Whether the value shown is the last known one while a newer one is computed.
398};
399
400struct NodeData;
401
402/// @brief A view-tree node: immutable, shared data.
403using Node = std::shared_ptr<NodeData const>;
404
405/// @brief Static or bound text.
406struct Text {
407 /// @brief The text, UTF-8.
409 /// @brief The style.
411 /// @brief Read-only, required, errors and stale.
413 /// @brief Visibility, enablement, layout, drag-and-drop.
415};
416
417/// @brief A push button.
418struct Button {
419 /// @brief The caption.
421 /// @brief How much it stands out; set once.
423 /// @brief Called on activation.
425 /// @brief Visibility, enablement, layout, drag-and-drop.
427 /// @brief The name of an icon in the theme's icon set, drawn beside the caption; empty shows none. Set once.
428 std::string icon;
429 /// @brief A push button or a list entry; set once.
431 /// @brief Whether a list entry is the current one. Only an `Item` takes it: the mount refuses a bound or `true`
432 /// value on a push button.
434};
435
436/// @brief An editable text field. The backend never reports its own `setText` back through `onChange`.
437struct TextInput {
438 /// @brief The text the field shows.
440 /// @brief Called with the new text after every user edit.
441 std::function<void(std::string)> onChange;
442 /// @brief Called with the text when the user submits (Enter in single-line mode).
443 std::function<void(std::string)> onSubmit;
444 /// @brief Called with the text when the user commits it: Enter in single-line mode, or focus leaving the field
445 /// after an edit.
446 std::function<void(std::string)> onCommit;
447 /// @brief Shown while the field is empty.
449 /// @brief Single-line, multiline or password; set once.
451 /// @brief The unit the input shows beside its text, such as `kg`; empty shows none. Only a single-line or
452 /// password input shows one: the mount refuses a multiline input with a unit.
454 /// @brief The name of an icon in the theme's icon set, drawn at the field's leading edge; empty shows none. Only
455 /// a single-line or password input shows one: the mount refuses a multiline input with an icon. Set once.
456 std::string icon;
457 /// @brief The fraction digits a number is entered with, for a backend or a kit to read; -1 when the input does
458 /// not hold a number. Set once; the mount refuses places on a multiline input.
459 int decimals = -1;
460 /// @brief The field's label: its visible caption while `showLabel`; a form's cell draws it otherwise, beside
461 /// the input. Empty: none.
463 /// @brief Whether the input draws `label` as a visible caption itself; set once. A form leaves it false, since
464 /// its cell draws the caption.
465 bool showLabel = false;
466 /// @brief Read-only, required, errors and stale.
468 /// @brief Visibility, enablement, layout, drag-and-drop.
470};
471
472/// @brief A labelled check box.
473struct Checkbox {
474 /// @brief The caption.
476 /// @brief Whether it is checked.
478 /// @brief Called with the new state when the user toggles it.
479 std::function<void(bool)> onToggle;
480 /// @brief Read-only, required, errors and stale.
482 /// @brief Visibility, enablement, layout, drag-and-drop.
484};
485
486/// @brief One option of a `Select`.
488 /// @brief The option's identity.
490 /// @brief The caption.
491 std::string label;
492
493 /// @brief Memberwise equality.
494 /// @return Whether key and label match.
495 bool operator==(SelectOption const&) const = default;
496};
497
498/// @brief A choice of one option.
499struct Select {
500 /// @brief The options, in order.
502 /// @brief The selected key, or none. A key outside `options` shows no selection.
504 /// @brief Called with the key the user chose.
505 std::function<void(Key)> onSelect;
506 /// @brief Called when the user opens the dropdown, whether or not it has options to show; a radio group is never
507 /// opened. A hint, such as retrying a fetch of the options that failed: a backend may not report it.
509 /// @brief Dropdown or radio; set once.
511 /// @brief The field's label: its visible caption while `showLabel`; a form's cell draws it otherwise, beside
512 /// the input. Empty: none.
514 /// @brief Whether the input draws `label` as a visible caption itself; set once. A form leaves it false, since
515 /// its cell draws the caption.
516 bool showLabel = false;
517 /// @brief Read-only, required, errors and stale.
519 /// @brief Visibility, enablement, layout, drag-and-drop.
521};
522
523/// @brief One entry of a `Menu`: its label and its handler in one place, so there are no parallel lists.
524///
525/// An entry with `items` opens a submenu and is not itself chosen.
526struct MenuItem {
527 /// @brief The caption.
529 /// @brief Called when the entry is chosen.
531 /// @brief The name of an icon in the theme's icon map; empty shows none. Set once.
532 std::string icon;
533 /// @brief The chord shown beside the label, such as `"Ctrl+S"`; empty shows none. Set once. It only shows the
534 /// chord: the binding that handles it is a `KeyBinding` on a node.
535 std::string keys;
536 /// @brief Engaged: the entry is checkable and shows this check mark; `nullopt`: not checkable. Choosing a
537 /// checkable entry runs `onSelect`, and the mark changes only when this binding does.
539 /// @brief Whether the entry can be chosen; a disabled entry's submenu cannot be opened.
541 /// @brief The submenu's entries; empty: the entry is chosen rather than opened.
542 std::vector<MenuItem> items;
543};
544
545/// @brief A vertical list of commands.
546struct Menu {
547 /// @brief The entries, in order; the list itself is set once, each entry's label, check mark and enablement
548 /// may be bound.
549 std::vector<MenuItem> items;
550 /// @brief Visibility, enablement, layout, drag-and-drop.
552};
553
554/// @brief The largest spacing step: a `gap` or `padding` names a step from 0 (none) to this, which each backend maps
555/// to its own distance (the theme's `space.<step>` on Qt Quick, cells on the terminal).
556inline constexpr int kMaxSpacingStep = 5;
557
558/// @brief A `gap` or `padding` as the spacing step a backend receives.
559/// @param value The value as written.
560/// @return @p value clamped to `[0, kMaxSpacingStep]`.
561[[nodiscard]] constexpr int spacingStep(int value) noexcept { return std::clamp(value, 0, kMaxSpacingStep); }
562
563/// @brief Children stacked top to bottom.
564struct Column {
565 /// @brief The children, in order; a null child is skipped.
566 std::vector<Node> children;
567 /// @brief Space between children: a spacing step, 0 to `kMaxSpacingStep`.
568 int gap = 0;
569 /// @brief Visibility, enablement, layout, drag-and-drop.
571 /// @brief Where the children sit across the column (horizontally); set once.
573 /// @brief Where the spare height goes while no shown child stretches; set once.
575};
576
577/// @brief Children laid out left to right.
578struct Row {
579 /// @brief The children, in order; a null child is skipped.
580 std::vector<Node> children;
581 /// @brief Space between children: a spacing step, 0 to `kMaxSpacingStep`.
582 int gap = 0;
583 /// @brief Visibility, enablement, layout, drag-and-drop.
585 /// @brief A table row's tone: disengaged when none was given; an engaged `Prop` whose value is `nullopt` has no
586 /// tone at the moment. Only `table`'s row view sets it; the mount refuses it on a row outside a table.
587 std::optional<Prop<std::optional<Tone>>> tone;
588 /// @brief Where the children sit across the row (vertically); set once.
590 /// @brief Where the spare width goes while no shown child stretches; set once.
592};
593
594/// @brief One cell of a `Grid`.
595struct GridCell {
596 /// @brief The cell's content; a null node leaves no cell.
598 /// @brief How many columns it spans.
599 int span = 1;
600};
601
602/// @brief Cells laid out row-major in a fixed number of columns.
603struct Grid {
604 /// @brief The column count.
605 int columns = 1;
606 /// @brief The cells, row-major.
607 std::vector<GridCell> cells;
608 /// @brief Space between cells: a spacing step, 0 to `kMaxSpacingStep`.
609 int gap = 0;
610 /// @brief Visibility, enablement, layout, drag-and-drop.
612 /// @brief Where each cell's content sits vertically in its row; set once. A grid's columns share its width, so
613 /// there is no spare length to justify.
615};
616
617/// @brief Empty space that takes what its layout hints ask for.
618struct Spacer {
619 /// @brief Visibility, enablement, layout, drag-and-drop.
621};
622
623/// @brief A titled frame around one child; a collapsible one is an accordion section.
624struct Panel {
625 /// @brief The title.
627 /// @brief Shown beside the title, collapsed or not; may be null.
629 /// @brief Space between frame and child: a spacing step, 0 to `kMaxSpacingStep`.
630 int padding = 0;
631 /// @brief The content; may be null.
633 /// @brief Whether the user can collapse it; set once.
634 bool collapsible = false;
635 /// @brief Whether it is collapsed; used only when `collapsible`.
637 /// @brief Called with the collapsed state the user asked for; used only when `collapsible`.
638 std::function<void(bool)> onToggle;
639 /// @brief Visibility, enablement, layout, drag-and-drop.
641};
642
643/// @brief A scrollable viewport around one child.
644struct Scroll {
645 /// @brief The content; may be null.
647 /// @brief The scroll direction; set once.
649 /// @brief Visibility, enablement, layout, drag-and-drop.
651};
652
653/// @brief One case of a `Switch`.
655 /// @brief The selector value this case answers.
657 /// @brief The content; may be null.
659};
660
661/// @brief Shows the case whose key the selector yields; remounts only when that key changes.
662struct Switch {
663 /// @brief Which case to show.
665 /// @brief The cases; the first one with the selected key wins.
666 std::vector<SwitchCase> cases;
667 /// @brief Shown when no case matches; may be null, and then nothing is shown.
669 /// @brief Visibility, enablement, layout, drag-and-drop.
671};
672
673/// @brief One page of `Tabs`.
674struct Tab {
675 /// @brief The tab's caption; set once.
676 std::string label;
677 /// @brief The page content, mounted the first time the tab is selected.
679};
680
681/// @brief A tab bar over pages.
682struct Tabs {
683 /// @brief The pages, in order; set once.
684 std::vector<Tab> tabs;
685 /// @brief The selected page's index; an index past the last page shows none.
687 /// @brief Called with the index the user picked, and with the page still shown when a picked page fails to mount.
688 std::function<void(std::size_t)> onSelect;
689 /// @brief Visibility, enablement, layout, drag-and-drop.
691};
692
693/// @brief A modal overlay; its content is mounted only while it is open.
694struct Dialog {
695 /// @brief Whether it is shown.
697 /// @brief The title.
699 /// @brief The content; may be null.
701 /// @brief Called when the user dismisses it (Esc on the TUI).
703 /// @brief Visibility, enablement, layout, drag-and-drop.
705};
706
707/// @brief A progress indicator.
708struct Busy {
709 /// @brief Whether it animates.
711 /// @brief A caption beside it.
713 /// @brief Visibility, enablement, layout, drag-and-drop.
715};
716
717namespace detail {
718
719/// @brief One mounted row of a ForEach or a Table: the row's signal, and the view built from it once.
720class RowSlot {
721public:
722 RowSlot() = default;
723 virtual ~RowSlot() = default;
724 RowSlot(RowSlot const&) = delete;
725 RowSlot& operator=(RowSlot const&) = delete;
726 RowSlot(RowSlot&&) = delete;
727 RowSlot& operator=(RowSlot&&) = delete;
728
729 /// @brief Sets the row's signal to an entry of its session's latest snapshot; an equal row notifies nobody.
730 /// @param index A position in that snapshot.
731 virtual void assign(std::size_t index) = 0;
732
733 /// @brief The view built for this row when the slot was made.
734 /// @return The row's node.
735 [[nodiscard]] virtual Node view() const = 0;
736};
737
738/// @brief One mount's use of a ForEach: the latest snapshot of the rows, and a way to make row slots from it.
739class ForEachSession {
740public:
741 ForEachSession() = default;
742 virtual ~ForEachSession() = default;
743 ForEachSession(ForEachSession const&) = delete;
744 ForEachSession& operator=(ForEachSession const&) = delete;
745 ForEachSession(ForEachSession&&) = delete;
746 ForEachSession& operator=(ForEachSession&&) = delete;
747
748 /// @brief Reads the rows (tracked, inside the mount's Effect), keeps them as the snapshot, and returns their
749 /// keys.
750 /// @return One key per row, in order, duplicates included.
751 [[nodiscard]] virtual std::vector<Key> pull() = 0;
752
753 /// @brief Makes the slot for one snapshot entry, building its view. The mount calls it untracked.
754 /// @param index A position in the latest snapshot.
755 /// @return The slot. It refers to this session, which must outlive it.
756 [[nodiscard]] virtual std::unique_ptr<RowSlot> makeRow(std::size_t index) = 0;
757};
758
759/// @brief A ForEach's row source, key function and row view, with the row type erased.
760class ForEachModel {
761public:
762 ForEachModel() = default;
763 virtual ~ForEachModel() = default;
764 ForEachModel(ForEachModel const&) = delete;
765 ForEachModel& operator=(ForEachModel const&) = delete;
766 ForEachModel(ForEachModel&&) = delete;
767 ForEachModel& operator=(ForEachModel&&) = delete;
768
769 /// @brief Starts one mount's session.
770 /// @param runtime The runtime the row signals belong to.
771 /// @return The session. It refers to this model, which must outlive it.
772 [[nodiscard]] virtual std::unique_ptr<ForEachSession> open(reactive::Runtime& runtime) const = 0;
773};
774
775/// @brief The typed model behind `forEach<RowT>` and `table<RowT>`.
776/// @tparam RowT The row type; copyable. With an `operator==`, an unchanged row notifies nobody; without one, every
777/// new snapshot notifies every kept row.
778template <typename RowT>
779class TypedForEach final : public ForEachModel {
780public:
781 /// @param rows Reads the rows; every signal it reads is a dependency of the ForEach.
782 /// @param keyOf A row's identity.
783 /// @param rowView Builds a row's view once, from a signal the mount keeps equal to the row.
784 /// @throws std::invalid_argument when any of the three is empty, as a `Prop` refuses an empty binding.
785 TypedForEach(std::function<std::vector<RowT>()> rows, std::function<Key(RowT const&)> keyOf,
786 std::function<Node(reactive::Signal<RowT> const&)> rowView)
787 : _rows{nonEmpty(std::move(rows))},
788 _keyOf{nonEmpty(std::move(keyOf))},
789 _rowView{nonEmpty(std::move(rowView))} {}
790
791 [[nodiscard]] std::unique_ptr<ForEachSession> open(reactive::Runtime& runtime) const override {
792 return std::make_unique<Session>(runtime, *this);
793 }
794
795private:
796 template <typename F>
797 [[nodiscard]] static F nonEmpty(F function) {
798 if (!function) {
799 throw std::invalid_argument{"ui::forEach: the rows, the key function and the row view must not be empty"};
800 }
801 return function;
802 }
803
804 class Slot final : public RowSlot {
805 public:
806 Slot(reactive::Runtime& runtime, std::vector<RowT> const& snapshot, std::size_t index,
807 TypedForEach const& model)
808 : _snapshot{&snapshot}, _row{runtime, snapshot.at(index)}, _view{model._rowView(_row)} {}
809
810 void assign(std::size_t index) override { _row.set(_snapshot->at(index)); }
811 [[nodiscard]] Node view() const override { return _view; }
812
813 private:
814 std::vector<RowT> const* _snapshot;
815 reactive::Signal<RowT> _row;
816 Node _view;
817 };
818
819 class Session final : public ForEachSession {
820 public:
821 Session(reactive::Runtime& runtime, TypedForEach const& model) : _rt{&runtime}, _model{&model} {}
822
823 [[nodiscard]] std::vector<Key> pull() override {
824 _snapshot = _model->_rows();
825 std::vector<Key> keys;
826 keys.reserve(_snapshot.size());
827 for (RowT const& entry : _snapshot) {
828 keys.push_back(_model->_keyOf(entry));
829 }
830 return keys;
831 }
832
833 [[nodiscard]] std::unique_ptr<RowSlot> makeRow(std::size_t index) override {
834 return std::make_unique<Slot>(*_rt, _snapshot, index, *_model);
835 }
836
837 private:
838 reactive::Runtime* _rt;
839 TypedForEach const* _model;
840 std::vector<RowT> _snapshot;
841 };
842
843 std::function<std::vector<RowT>()> _rows;
844 std::function<Key(RowT const&)> _keyOf;
845 std::function<Node(reactive::Signal<RowT> const&)> _rowView;
846};
847
848} // namespace detail
849
850/// @brief When a keyed row list realizes a widget only for the rows in or near the visible range, instead of for
851/// every row regardless of scroll position. A row's own reactive state (its signal, rebuilt from the
852/// session on every change) is unaffected either way and is never released while its key exists; this
853/// setting only decides whether a row's mounted widget is also kept alive off-screen.
855 /// @brief Below this many rows, every row's widget is realized regardless of scroll position, matching
856 /// today's behavior exactly; set once.
857 std::size_t threshold = 2000;
858 /// @brief How many rows beyond the visible range, on each side, keep a realized widget so a small scroll does
859 /// not have to realize a widget on every frame; set once.
860 std::size_t overscan = 10;
861};
862
863/// @brief One widget per row of a keyed collection, updated in place when a row with the same key changes.
864struct ForEach {
865 /// @brief The rows, the key function and the row view, type-erased; null shows no rows.
866 std::shared_ptr<detail::ForEachModel const> model;
867 /// @brief The direction rows are stacked in; set once.
869 /// @brief Space between rows: a spacing step, 0 to `kMaxSpacingStep`.
870 int gap = 0;
871 /// @brief Visibility, enablement, layout, drag-and-drop of the row container.
873};
874
875/// @brief One column of a `Table`.
877 /// @brief The header caption.
878 std::string label;
879 /// @brief The column's width request.
881 /// @brief The text style of this column's default cell; `Normal` renders unchanged. Has no effect on a column
882 /// with its own `.cell`, or on a `Table` constructed directly without going through the interpreter.
884};
885
886/// @brief Keyed rows of cells under column headers. Each row's view is a `Row` whose children are the cells, one
887/// per column.
888struct Table {
889 /// @brief The columns, in order; set once.
890 std::vector<TableColumn> columns;
891 /// @brief The rows, the key function and the row view, type-erased; null shows no rows.
892 std::shared_ptr<detail::ForEachModel const> rows;
893 /// @brief Whether the header row is drawn; set once. `false` suits several tables sharing one header, or a
894 /// header a document draws itself.
895 bool showHeader = true;
896 /// @brief The row count and margin above which a row's widget is realized only near the visible range; set
897 /// once. A row's own reactive state is unaffected either way. Only a backend that implements
898 /// `TableWidget::setOnVisibleRangeChanged` ever dewindows a row; every other backend realizes every
899 /// row regardless of this setting, exactly as before it existed.
901 /// @brief How many rows the user can select; set once.
903 /// @brief The selected rows' keys. A key with no row selects nothing until such a row appears.
905 /// @brief Called with the keys the user selected.
906 std::function<void(std::vector<Key>)> onSelectionChange;
907 /// @brief Called with a row's key when the user activates it (Enter or a double click).
908 std::function<void(Key)> onActivate;
909 /// @brief Visibility, enablement, layout, drag-and-drop.
911};
912
913/// @brief A date, or a date and time, edited in a display zone and reported as an instant.
915 /// @brief The instant shown; `nullopt` (or an empty `Timestamp`) shows an empty field.
917 /// @brief Called with the instant the user entered, or `nullopt` when they cleared the field.
918 std::function<void(std::optional<morph::time::Timestamp>)> onChange;
919 /// @brief Date or date and time; set once.
921 /// @brief The display zone's offset from UTC, in minutes; set once.
923 /// @brief The field's label: its visible caption while `showLabel`; a form's cell draws it otherwise, beside
924 /// the input. Empty: none.
926 /// @brief Whether the input draws `label` as a visible caption itself; set once. A form leaves it false, since
927 /// its cell draws the caption.
928 bool showLabel = false;
929 /// @brief Read-only, required, errors and stale.
931 /// @brief Visibility, enablement, layout, drag-and-drop.
933};
934
935/// @brief An integer chosen on a range.
936struct Slider {
937 /// @brief The value shown.
939 /// @brief The smallest value; set once.
940 std::int64_t minimum = 0;
941 /// @brief The largest value; set once.
942 std::int64_t maximum = 100;
943 /// @brief The increment between neighbouring values; set once.
944 std::int64_t step = 1;
945 /// @brief Called with the value the user moved to.
946 std::function<void(std::int64_t)> onChange;
947 /// @brief Called with the value a finished move ended on: a release after a drag, a key step, or a wheel step.
948 std::function<void(std::int64_t)> onCommit;
949 /// @brief Read-only, required, errors and stale.
951 /// @brief Visibility, enablement, layout, drag-and-drop.
953};
954
955/// @brief A file path, typed or chosen in the platform's file dialog.
957 /// @brief The path shown.
959 /// @brief Open an existing file or name one to save; set once.
961 /// @brief Called with the path the user picked.
962 std::function<void(std::string)> onPicked;
963 /// @brief Read-only, required, errors and stale.
965 /// @brief Visibility, enablement, layout, drag-and-drop.
967};
968
969/// @brief A button inside another kind, such as a banner's action: its caption beside its handler.
971 /// @brief The caption; empty shows no button.
973 /// @brief Called when the button is activated.
975};
976
977/// @brief A message across the top of a region, with an optional action and an optional dismissal.
978struct Banner {
979 /// @brief What the message conveys.
981 /// @brief The message.
983 /// @brief The button beside the message; an empty label shows none.
985 /// @brief Whether the user can dismiss it; set once.
986 bool dismissible = false;
987 /// @brief Called when the user dismisses it. The banner stays shown until the application hides it.
989 /// @brief Visibility, enablement, layout, drag-and-drop.
991};
992
993/// @brief A short label marking a state, such as a count or a status.
994struct Badge {
995 /// @brief What the mark conveys.
997 /// @brief The label.
999 /// @brief The name of an icon in the theme's icon map; empty shows none. Set once.
1000 std::string icon;
1001 /// @brief Visibility, enablement, layout, drag-and-drop.
1003};
1004
1005/// @brief A progress bar.
1006struct Progress {
1007 /// @brief How far along, from 0 to 1; a value outside is shown at the nearer end. `nullopt`: indeterminate.
1009 /// @brief A caption beside the bar.
1011 /// @brief Visibility, enablement, layout, drag-and-drop.
1013};
1014
1015/// @brief One step of a `Steps` indicator.
1016struct Step {
1017 /// @brief The caption.
1018 std::string label;
1019 /// @brief Where it stands.
1021
1022 /// @brief Memberwise equality.
1023 /// @return Whether label and state match.
1024 bool operator==(Step const&) const = default;
1025};
1026
1027/// @brief The steps of a process in order, each with its state.
1028struct Steps {
1029 /// @brief The steps.
1031 /// @brief The step to highlight, by position; `nullopt` or a position past the last highlights none.
1033 /// @brief Visibility, enablement, layout, drag-and-drop.
1035};
1036
1037/// @brief One row of a `KeyValue` list.
1039 /// @brief The caption.
1040 std::string label;
1041 /// @brief The value, as text.
1042 std::string value;
1043
1044 /// @brief Memberwise equality.
1045 /// @return Whether label and value match.
1046 bool operator==(KeyValueItem const&) const = default;
1047};
1048
1049/// @brief Captions beside their values, one pair per row.
1050struct KeyValue {
1051 /// @brief The pairs, in order.
1053 /// @brief Visibility, enablement, layout, drag-and-drop.
1055};
1056
1057/// @brief What a region shows when it has nothing to show: a title, an explanation and an optional action.
1059 /// @brief The title.
1061 /// @brief The explanation.
1063 /// @brief The name of an icon in the theme's icon map; empty shows none. Set once.
1064 std::string icon;
1065 /// @brief The button below the text; an empty label shows none.
1067 /// @brief Visibility, enablement, layout, drag-and-drop.
1069};
1070
1071/// @brief A panel that slides in from one edge over the content: a `Dialog` with a side, and the same rules.
1072struct Drawer {
1073 /// @brief Whether it is shown.
1075 /// @brief The title.
1077 /// @brief The content; may be null.
1079 /// @brief Called when the user dismisses it (Esc on the TUI).
1081 /// @brief The edge it slides in from; set once.
1083 /// @brief Visibility, enablement, layout, drag-and-drop.
1085};
1086
1087/// @brief A row of buttons in the footer of the dialog or drawer whose content holds it: what a document's overlay
1088/// `actions` lower to. The mount refuses one anywhere else.
1090 /// @brief The buttons, in order; a null child is skipped.
1091 std::vector<Node> children;
1092 /// @brief Visibility, enablement, layout, drag-and-drop of the row.
1094};
1095
1096/// @brief Panes side by side, or stacked, with handles the user drags to share the space between them.
1097struct Splitter {
1098 /// @brief The panes, in order; a null child is skipped.
1099 std::vector<Node> children;
1100 /// @brief The axis the panes are laid out along; set once.
1102 /// @brief Each pane's size, in backend units, in order; empty shares the space equally. A list whose length
1103 /// differs from the number of panes is shared out as an empty one.
1105 /// @brief Called with every pane's size after the user dragged a handle.
1106 std::function<void(std::vector<int>)> onResize;
1107 /// @brief Visibility, enablement, layout, drag-and-drop.
1109};
1110
1111/// @brief A titled section the user opens and closes. Its header stays usable while it is closed.
1113 /// @brief The title.
1115 /// @brief Whether the content is shown.
1117 /// @brief Called with the state the user asked for.
1118 std::function<void(bool)> onToggle;
1119 /// @brief Shown beside the title, open or closed; may be null.
1121 /// @brief The content; may be null. Mounted once, and hidden while closed.
1123 /// @brief Visibility, enablement, layout, drag-and-drop.
1125};
1126
1127/// @brief A region that takes files dropped from the operating system.
1128struct DropZone {
1129 /// @brief The extensions it takes, such as `".csv"`, compared without regard to case; empty takes every file.
1130 /// Set once.
1131 std::vector<std::string> accept;
1132 /// @brief Whether a drop may carry more than one file; set once.
1133 bool multiple = false;
1134 /// @brief Called with the paths of a drop it took. A drop with a file outside `accept`, or with several files
1135 /// when not `multiple`, is refused whole.
1136 std::function<void(std::vector<std::string>)> onDrop;
1137 /// @brief The content shown inside the region; may be null.
1139 /// @brief Visibility, enablement, layout, drag-and-drop.
1141};
1142
1143/// @brief One prop a loaded custom component is given.
1145 /// @brief The prop's name, as the component declares it.
1146 std::string name;
1147 /// @brief Its value: text for `string`, `int`, `exact` and `json`, which cross as text so that no value loses
1148 /// precision; a `bool`; or a `number`.
1149 std::variant<Prop<std::string>, Prop<bool>, Prop<double>> value;
1150};
1151
1152/// @brief A component the backend may load (spec 5 §10), with the built-in nodes shown when it does not.
1153///
1154/// `Mounted` asks `IViewBackend::createCustom` first and mounts the fallback only when the backend loads nothing.
1155/// The fallback carries its own `Common`.
1156struct Custom {
1157 /// @brief The component's name, as the document's `components` declares it.
1158 std::string name;
1159 /// @brief The built-in nodes shown in its place; never null.
1161 /// @brief The props a loaded component is given, bound as any other property is.
1162 std::vector<CustomProp> props;
1163 /// @brief The lowest version of a host component that will do.
1164 int minVersion = 0;
1165 /// @brief Called with an event a loaded component raises: its name and its payload as the backend reports it.
1166 std::function<void(std::string event, std::string payload)> onEvent;
1167};
1168
1169/// @brief What a `Node` points at: exactly one node kind.
1177
1178namespace detail {
1179
1180/// @brief Wraps one node aggregate into a shared node.
1181/// @tparam Kind The node aggregate type.
1182/// @param spec The node.
1183/// @return The shared node.
1184template <typename Kind>
1185[[nodiscard]] Node makeNode(Kind spec) {
1186 return std::make_shared<NodeData const>(NodeData{.kind = std::move(spec)});
1187}
1188
1189} // namespace detail
1190
1191/// @brief Static or bound text.
1192/// @param spec The node.
1193/// @return The node.
1194[[nodiscard]] inline Node text(Text spec) { return detail::makeNode(std::move(spec)); }
1195
1196/// @brief A push button.
1197/// @param spec The node.
1198/// @return The node.
1199[[nodiscard]] inline Node button(Button spec) { return detail::makeNode(std::move(spec)); }
1200
1201/// @brief An editable text field.
1202/// @param spec The node.
1203/// @return The node.
1204[[nodiscard]] inline Node textInput(TextInput spec) { return detail::makeNode(std::move(spec)); }
1205
1206/// @brief A labelled check box.
1207/// @param spec The node.
1208/// @return The node.
1209[[nodiscard]] inline Node checkbox(Checkbox spec) { return detail::makeNode(std::move(spec)); }
1210
1211/// @brief A choice of one option.
1212/// @param spec The node.
1213/// @return The node.
1214[[nodiscard]] inline Node select(Select spec) { return detail::makeNode(std::move(spec)); }
1215
1216/// @brief A list of commands.
1217/// @param spec The node.
1218/// @return The node.
1219[[nodiscard]] inline Node menu(Menu spec) { return detail::makeNode(std::move(spec)); }
1220
1221/// @brief Children stacked top to bottom.
1222/// @param spec The node.
1223/// @return The node.
1224[[nodiscard]] inline Node column(Column spec) { return detail::makeNode(std::move(spec)); }
1225
1226/// @brief Children laid out left to right.
1227/// @param spec The node.
1228/// @return The node.
1229[[nodiscard]] inline Node row(Row spec) { return detail::makeNode(std::move(spec)); }
1230
1231/// @brief Cells in a fixed number of columns.
1232/// @param spec The node.
1233/// @return The node.
1234[[nodiscard]] inline Node grid(Grid spec) { return detail::makeNode(std::move(spec)); }
1235
1236/// @brief Empty space.
1237/// @param spec The node.
1238/// @return The node.
1239[[nodiscard]] inline Node spacer(Spacer spec = {}) { return detail::makeNode(std::move(spec)); }
1240
1241/// @brief A titled frame, optionally collapsible.
1242/// @param spec The node.
1243/// @return The node.
1244[[nodiscard]] inline Node panel(Panel spec) { return detail::makeNode(std::move(spec)); }
1245
1246/// @brief A scrollable viewport.
1247/// @param spec The node.
1248/// @return The node.
1249[[nodiscard]] inline Node scroll(Scroll spec) { return detail::makeNode(std::move(spec)); }
1250
1251/// @brief Shows the case the selector picks. Spelled `switchOf` because `switch` is a keyword.
1252/// @param spec The node.
1253/// @return The node.
1254[[nodiscard]] inline Node switchOf(Switch spec) { return detail::makeNode(std::move(spec)); }
1255
1256/// @brief A tab bar over pages.
1257/// @param spec The node.
1258/// @return The node.
1259[[nodiscard]] inline Node tabs(Tabs spec) { return detail::makeNode(std::move(spec)); }
1260
1261/// @brief A modal overlay.
1262/// @param spec The node.
1263/// @return The node.
1264[[nodiscard]] inline Node dialog(Dialog spec) { return detail::makeNode(std::move(spec)); }
1265
1266/// @brief A progress indicator.
1267/// @param spec The node.
1268/// @return The node.
1269[[nodiscard]] inline Node busy(Busy spec) { return detail::makeNode(std::move(spec)); }
1270
1271/// @brief A date or date-and-time field.
1272/// @param spec The node.
1273/// @return The node.
1274[[nodiscard]] inline Node dateTimeInput(DateTimeInput spec) { return detail::makeNode(std::move(spec)); }
1275
1276/// @brief An integer on a range.
1277/// @param spec The node.
1278/// @return The node.
1279[[nodiscard]] inline Node slider(Slider spec) { return detail::makeNode(std::move(spec)); }
1280
1281/// @brief A file path field.
1282/// @param spec The node.
1283/// @return The node.
1284[[nodiscard]] inline Node filePicker(FilePicker spec) { return detail::makeNode(std::move(spec)); }
1285
1286/// @brief A message across the top of a region.
1287/// @param spec The node.
1288/// @return The node.
1289[[nodiscard]] inline Node banner(Banner spec) { return detail::makeNode(std::move(spec)); }
1290
1291/// @brief A short label marking a state.
1292/// @param spec The node.
1293/// @return The node.
1294[[nodiscard]] inline Node badge(Badge spec) { return detail::makeNode(std::move(spec)); }
1295
1296/// @brief A progress bar.
1297/// @param spec The node.
1298/// @return The node.
1299[[nodiscard]] inline Node progress(Progress spec) { return detail::makeNode(std::move(spec)); }
1300
1301/// @brief The steps of a process.
1302/// @param spec The node.
1303/// @return The node.
1304[[nodiscard]] inline Node steps(Steps spec) { return detail::makeNode(std::move(spec)); }
1305
1306/// @brief Captions beside their values.
1307/// @param spec The node.
1308/// @return The node.
1309[[nodiscard]] inline Node keyValue(KeyValue spec) { return detail::makeNode(std::move(spec)); }
1310
1311/// @brief What a region shows when it has nothing to show.
1312/// @param spec The node.
1313/// @return The node.
1314[[nodiscard]] inline Node emptyState(EmptyState spec) { return detail::makeNode(std::move(spec)); }
1315
1316/// @brief A panel that slides in from one edge.
1317/// @param spec The node.
1318/// @return The node.
1319[[nodiscard]] inline Node drawer(Drawer spec) { return detail::makeNode(std::move(spec)); }
1320
1321/// @brief An action bar node.
1322/// @param spec The bar.
1323/// @return The node.
1324[[nodiscard]] inline Node actionBar(ActionBar spec) { return detail::makeNode(std::move(spec)); }
1325
1326/// @brief Panes with handles the user drags.
1327/// @param spec The node.
1328/// @return The node.
1329[[nodiscard]] inline Node splitter(Splitter spec) { return detail::makeNode(std::move(spec)); }
1330
1331/// @brief A titled section the user opens and closes.
1332/// @param spec The node.
1333/// @return The node.
1334[[nodiscard]] inline Node collapsible(Collapsible spec) { return detail::makeNode(std::move(spec)); }
1335
1336/// @brief A region that takes dropped files.
1337/// @param spec The node.
1338/// @return The node.
1339[[nodiscard]] inline Node dropZone(DropZone spec) { return detail::makeNode(std::move(spec)); }
1340
1341/// @brief A component loaded by name, mounted by `Mounted` as its fallback.
1342/// @param spec The node.
1343/// @return The node.
1344/// @throws std::invalid_argument when the fallback is null: a renderer that cannot load the component has nothing
1345/// else to show.
1346[[nodiscard]] inline Node custom(Custom spec) {
1347 if (!spec.fallback) {
1348 throw std::invalid_argument{"morph::ui::custom: the fallback of a custom node is null"};
1349 }
1350 return detail::makeNode(std::move(spec));
1351}
1352
1353/// @brief A Switch over an enumeration: each enumerator becomes the `int64` key of its case.
1354///
1355/// Enumerators are compared as `int64`: an unsigned value above `INT64_MAX` wraps to a negative key, and distinct
1356/// enumerators still give distinct keys.
1357/// @tparam E The enumeration.
1358/// @param selector Which enumerator to show; every signal it reads is a dependency.
1359/// @param cases One node per enumerator shown.
1360/// @param fallback Shown when no case matches; may be null.
1361/// @return The node.
1362/// @throws std::invalid_argument when @p selector is empty, as a `Prop` refuses an empty binding.
1363template <typename E>
1364 requires std::is_enum_v<E>
1365[[nodiscard]] Node switchOn(std::function<E()> selector, std::vector<std::pair<E, Node>> cases, Node fallback = {}) {
1366 if (!selector) {
1367 throw std::invalid_argument{"ui::switchOn: the selector must not be empty"};
1368 }
1369 std::vector<SwitchCase> keyed;
1370 keyed.reserve(cases.size());
1371 for (auto& [value, node] : cases) {
1372 keyed.push_back(SwitchCase{.key = Key{static_cast<std::int64_t>(value)}, .node = std::move(node)});
1373 }
1374 Switch spec{
1375 .selector = [current = std::move(selector)] { return Key{static_cast<std::int64_t>(current())}; },
1376 .cases = std::move(keyed),
1377 };
1378 spec.fallback = std::move(fallback);
1379 return switchOf(std::move(spec));
1380}
1381
1382/// @brief One widget per row of a keyed collection read through a binding (a `Query`'s `value()` included).
1383///
1384/// A kept key keeps its widget, which a changed row updates in place; a new key is mounted, a gone key unmounted, and
1385/// the rows are brought into the snapshot's order by moving as few widgets as possible.
1386/// @tparam RowT The row type; copyable.
1387/// @param rows Reads the rows; every signal it reads is a dependency.
1388/// @param keyOf A row's identity; a later duplicate of a key is reported and refused, and the first one is kept.
1389/// @param rowView Builds a row's view once, from a signal the mount keeps equal to the row. Read the signal inside
1390/// bindings: what the view reads directly while it is built is read once and never again.
1391/// @param axis The stacking direction.
1392/// @param gap Space between rows.
1393/// @return The node.
1394/// @throws std::invalid_argument when @p rows, @p keyOf or @p rowView is empty.
1395template <typename RowT>
1396[[nodiscard]] Node forEach(std::function<std::vector<RowT>()> rows, std::function<Key(RowT const&)> keyOf,
1397 std::function<Node(reactive::Signal<RowT> const&)> rowView, Axis axis = Axis::Vertical,
1398 int gap = 0) {
1400 .model =
1401 std::make_shared<detail::TypedForEach<RowT> const>(std::move(rows), std::move(keyOf), std::move(rowView)),
1402 .axis = axis,
1403 .gap = gap,
1404 });
1405}
1406
1407/// @brief One widget per row of a keyed collection held in a signal.
1408/// @tparam RowT The row type; copyable.
1409/// @param rows The signal; it must outlive every mount of the node.
1410/// @param keyOf A row's identity; a later duplicate of a key is reported and refused, and the first one is kept.
1411/// @param rowView Builds a row's view once, from a signal the mount keeps equal to the row.
1412/// @param axis The stacking direction.
1413/// @param gap Space between rows.
1414/// @return The node.
1415/// @throws std::invalid_argument when @p keyOf or @p rowView is empty.
1416template <typename RowT>
1417[[nodiscard]] Node forEach(reactive::Signal<std::vector<RowT>> const& rows, std::function<Key(RowT const&)> keyOf,
1418 std::function<Node(reactive::Signal<RowT> const&)> rowView, Axis axis = Axis::Vertical,
1419 int gap = 0) {
1420 return forEach<RowT>([&rows] { return rows.get(); }, std::move(keyOf), std::move(rowView), axis, gap);
1421}
1422
1423/// @brief Everything about a `table` beyond its columns and rows.
1425 /// @brief Whether the header row is drawn.
1426 bool showHeader = true;
1427 /// @brief The row count and margin above which a row's widget is realized only near the visible range.
1429 /// @brief How many rows the user can select.
1431 /// @brief The selected rows' keys.
1433 /// @brief Called with the keys the user selected.
1434 std::function<void(std::vector<Key>)> onSelectionChange;
1435 /// @brief Called with a row's key when the user activates it.
1436 std::function<void(Key)> onActivate;
1437 /// @brief Visibility, enablement, layout, drag-and-drop of the table.
1439};
1440
1441/// @brief What a table's row view builds for one row: its cells, one per column, and its tone.
1443 /// @brief The cells, one non-null node per column.
1444 std::vector<Node> cells;
1445 /// @brief The row's tone, bound in the row's own scope; disengaged for none.
1446 std::optional<Prop<std::optional<Tone>>> tone;
1447};
1448
1449/// @brief A table of keyed rows: one `Row` of cells per row, under the column headers.
1450///
1451/// Rows are mounted, updated, unmounted and reordered as `forEach` does. A row whose cells are not exactly one
1452/// non-null node per column would shift the columns after it, so it is refused as a row that fails to mount is:
1453/// reported, left out, and tried again on the next change of the rows.
1454/// @tparam RowT The row type; copyable.
1455/// @param columns The columns, in order.
1456/// @param rows Reads the rows; every signal it reads is a dependency.
1457/// @param keyOf A row's identity; a later duplicate of a key is reported and refused, and the first one is kept.
1458/// @param cells Builds a row's cells once, one per column, from a signal the mount keeps equal to the row. Read the
1459/// signal inside bindings: what the cells read directly while they are built is read once and never again.
1460/// @param options Selection, activation and `Common`.
1461/// @return The node.
1462/// @throws std::invalid_argument when @p rows, @p keyOf or @p cells is empty.
1463template <typename RowT>
1464[[nodiscard]] Node table(std::vector<TableColumn> columns, std::function<std::vector<RowT>()> rows,
1465 std::function<Key(RowT const&)> keyOf,
1466 std::function<std::vector<Node>(reactive::Signal<RowT> const&)> cells,
1467 TableOptions options = {}) {
1468 if (!rows || !keyOf || !cells) {
1469 throw std::invalid_argument{"ui::table: the rows, the key function and the cells must not be empty"};
1470 }
1471 std::function<TableRowView(reactive::Signal<RowT> const&)> rowView =
1472 [cellsOf = std::move(cells)](reactive::Signal<RowT> const& entry) {
1473 return TableRowView{.cells = cellsOf(entry), .tone = std::nullopt};
1474 };
1475 return table<RowT>(std::move(columns), std::move(rows), std::move(keyOf), std::move(rowView), std::move(options));
1476}
1477
1478/// @brief A table of keyed rows whose row view gives each row its tone as well as its cells.
1479///
1480/// As the other `table`, with @p rowView answering a `TableRowView`: the tone binds in the row's own scope, and the
1481/// table is told it after the row's key and on every change.
1482/// @tparam RowT The row type; copyable.
1483/// @param columns The columns, in order.
1484/// @param rows Reads the rows; every signal it reads is a dependency.
1485/// @param keyOf A row's identity; a later duplicate of a key is reported and refused, and the first one is kept.
1486/// @param rowView Builds a row's cells, one per column, and its tone, once, from a signal the mount keeps equal to
1487/// the row.
1488/// @param options Selection, activation and `Common`.
1489/// @return The node.
1490/// @throws std::invalid_argument when @p rows, @p keyOf or @p rowView is empty.
1491template <typename RowT>
1492[[nodiscard]] Node table(std::vector<TableColumn> columns, std::function<std::vector<RowT>()> rows,
1493 std::function<Key(RowT const&)> keyOf,
1494 std::function<TableRowView(reactive::Signal<RowT> const&)> rowView,
1495 TableOptions options = {}) {
1496 if (!rows || !keyOf || !rowView) {
1497 throw std::invalid_argument{"ui::table: the rows, the key function and the row view must not be empty"};
1498 }
1499 std::function<Node(reactive::Signal<RowT> const&)> rowNode =
1500 [viewOf = std::move(rowView), count = columns.size()](reactive::Signal<RowT> const& entry) {
1501 TableRowView made = viewOf(entry);
1502 if (made.cells.size() != count ||
1503 std::ranges::any_of(made.cells, [](Node const& cell) { return cell == nullptr; })) {
1504 throw std::invalid_argument{"ui::table: a row needs exactly one non-null cell per column"};
1505 }
1506 return row(Row{.children = std::move(made.cells), .tone = std::move(made.tone)});
1507 };
1508 Table spec{
1509 .rows =
1510 std::make_shared<detail::TypedForEach<RowT> const>(std::move(rows), std::move(keyOf), std::move(rowNode)),
1511 .showHeader = options.showHeader,
1512 .virtualize = options.virtualize,
1513 .selectionMode = options.selectionMode,
1514 };
1515 spec.columns = std::move(columns);
1516 spec.selection = std::move(options.selection);
1517 spec.onSelectionChange = std::move(options.onSelectionChange);
1518 spec.onActivate = std::move(options.onActivate);
1519 spec.common = std::move(options.common);
1520 return detail::makeNode(std::move(spec));
1521}
1522
1523} // namespace morph::ui
morph::ui::Align and morph::ui::Justify: where a container places its children across its axis and al...
Justify
Where a stack places its spare length along its axis, while no shown child stretches.
Definition alignment.hpp:23
@ Start
Packed at the start.
Definition alignment.hpp:24
Align
Where a child is placed across its container's axis: a column's horizontally, a row's vertically,...
Definition alignment.hpp:15
@ End
At the end: right in a column, bottom in a row or grid.
Definition alignment.hpp:19
@ Start
At the start: left in a column, top in a row or grid.
Definition alignment.hpp:17
@ Stretch
A content child fills the cross extent; one its maximum stops sits at the start.
Definition alignment.hpp:16
Handle< std::int64_t > count(AnyHandle const &list)
count: a list's length.
Definition author.hpp:1000
morph::ui::ButtonEmphasis: how much a button stands out. The view tree, the authoring API and the bac...
ButtonEmphasis
How much a button stands out; each backend maps it to its theme.
@ Normal
An ordinary action.
morph::ui::ButtonStyle: whether a button is drawn as a push button or as an entry of a list....
ButtonStyle
How a button is drawn; it behaves the same either way.
@ Push
A framed button as wide as its label.
The owner of a signal graph: every node takes one, and every operation runs on its owner.
Definition runtime.hpp:70
A source value. Reading it with get() inside a tracked run subscribes that run.
Definition signal.hpp:49
A node property: a constant, or a slot — a reactive read, with a stable id when it was given one.
Definition view.hpp:106
T const & constant() const
The constant. Only valid when !isBound().
Definition view.hpp:160
Prop(SlotBinding< F > binding)
A slot with an id, as ui::slot makes it.
Definition view.hpp:148
std::optional< SlotId > slotId() const noexcept
The slot's id.
Definition view.hpp:169
std::function< T()> const & binding() const
The slot's read. Only valid when isBound().
Definition view.hpp:165
bool isBound() const noexcept
Whether this is a slot.
Definition view.hpp:155
Prop(F binding)
A slot without an id.
Definition view.hpp:138
T evaluate() const
The current value: the constant, or the slot read once.
Definition view.hpp:176
Prop(U &&value)
A constant.
Definition view.hpp:124
Prop()
The value-initialised constant.
Definition view.hpp:109
virtual std::unique_ptr< ForEachSession > open(reactive::Runtime &runtime) const =0
Starts one mount's session.
virtual std::unique_ptr< RowSlot > makeRow(std::size_t index)=0
Makes the slot for one snapshot entry, building its view. The mount calls it untracked.
virtual std::vector< Key > pull()=0
Reads the rows (tracked, inside the mount's Effect), keeps them as the snapshot, and returns their ke...
virtual void assign(std::size_t index)=0
Sets the row's signal to an entry of its session's latest snapshot; an equal row notifies nobody.
virtual Node view() const =0
The view built for this row when the slot was made.
std::unique_ptr< ForEachSession > open(reactive::Runtime &runtime) const override
Starts one mount's session.
Definition view.hpp:791
TypedForEach(std::function< std::vector< RowT >()> rows, std::function< Key(RowT const &)> keyOf, std::function< Node(reactive::Signal< RowT > const &)> rowView)
Definition view.hpp:785
Whether From may become a constant Prop<To>: an implicit conversion that does not silently change the...
Definition view.hpp:89
UTC timestamps for morph actions.
@ Open
{"open": "dialog"}
Definition document.hpp:247
@ Pending
{"pending": "q"}: whether a query or mutation is in flight.
@ Save
Run the screen's save, and close it only if it is then clean.
Definition host.hpp:203
Key keyOf(Value const &value)
A value as a view key: an int or a string as itself, anything else as its JSON text.
morph::reactive::Runtime: the owner, batching and flush scheduling of a signal graph.
Signal<T>, Computed<T> and Effect: the nodes of a reactive graph.
What assistive technology announces for a node.
Definition view.hpp:331
Prop< std::string > name
The accessible name; empty leaves the widget's own text as its name.
Definition view.hpp:333
std::string role
The role, such as "heading"; empty leaves the kind's own role. Set once.
Definition view.hpp:335
A row of buttons in the footer of the dialog or drawer whose content holds it: what a document's over...
Definition view.hpp:1089
Common common
Visibility, enablement, layout, drag-and-drop of the row.
Definition view.hpp:1093
std::vector< Node > children
The buttons, in order; a null child is skipped.
Definition view.hpp:1091
A button inside another kind, such as a banner's action: its caption beside its handler.
Definition view.hpp:970
Prop< std::string > label
The caption; empty shows no button.
Definition view.hpp:972
Action onClick
Called when the button is activated.
Definition view.hpp:974
A short label marking a state, such as a count or a status.
Definition view.hpp:994
Prop< std::string > text
The label.
Definition view.hpp:998
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1002
Prop< Tone > tone
What the mark conveys.
Definition view.hpp:996
std::string icon
The name of an icon in the theme's icon map; empty shows none. Set once.
Definition view.hpp:1000
A message across the top of a region, with an optional action and an optional dismissal.
Definition view.hpp:978
Prop< std::string > text
The message.
Definition view.hpp:982
bool dismissible
Whether the user can dismiss it; set once.
Definition view.hpp:986
ActionButton action
The button beside the message; an empty label shows none.
Definition view.hpp:984
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:990
Action onDismiss
Called when the user dismisses it. The banner stays shown until the application hides it.
Definition view.hpp:988
Prop< Tone > tone
What the message conveys.
Definition view.hpp:980
A progress indicator.
Definition view.hpp:708
Prop< std::string > label
A caption beside it.
Definition view.hpp:712
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:714
Prop< bool > active
Whether it animates.
Definition view.hpp:710
A push button.
Definition view.hpp:418
Prop< bool > selected
Whether a list entry is the current one. Only an Item takes it: the mount refuses a bound or true val...
Definition view.hpp:433
std::string icon
The name of an icon in the theme's icon set, drawn beside the caption; empty shows none....
Definition view.hpp:428
Prop< std::string > label
The caption.
Definition view.hpp:420
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:426
ButtonStyle style
A push button or a list entry; set once.
Definition view.hpp:430
Action onClick
Called on activation.
Definition view.hpp:424
ButtonEmphasis emphasis
How much it stands out; set once.
Definition view.hpp:422
A labelled check box.
Definition view.hpp:473
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:483
std::function< void(bool)> onToggle
Called with the new state when the user toggles it.
Definition view.hpp:479
Prop< std::string > label
The caption.
Definition view.hpp:475
Prop< bool > checked
Whether it is checked.
Definition view.hpp:477
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:481
A titled section the user opens and closes. Its header stays usable while it is closed.
Definition view.hpp:1112
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1124
Node header
Shown beside the title, open or closed; may be null.
Definition view.hpp:1120
Prop< bool > open
Whether the content is shown.
Definition view.hpp:1116
std::function< void(bool)> onToggle
Called with the state the user asked for.
Definition view.hpp:1118
Node child
The content; may be null. Mounted once, and hidden while closed.
Definition view.hpp:1122
Prop< std::string > title
The title.
Definition view.hpp:1114
Children stacked top to bottom.
Definition view.hpp:564
Align align
Where the children sit across the column (horizontally); set once.
Definition view.hpp:572
Justify justify
Where the spare height goes while no shown child stretches; set once.
Definition view.hpp:574
int gap
Space between children: a spacing step, 0 to kMaxSpacingStep.
Definition view.hpp:568
std::vector< Node > children
The children, in order; a null child is skipped.
Definition view.hpp:566
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:570
The part every node carries: visibility, enablement, layout, drag-and-drop, accessibility,...
Definition view.hpp:348
Prop< std::string > tooltip
Text shown while the pointer rests on the widget; empty shows none.
Definition view.hpp:369
Prop< std::optional< Key > > dragKey
Engaged: the widget can be dragged, and a drop delivers this key.
Definition view.hpp:356
Accessibility a11y
The accessible name and role.
Definition view.hpp:362
std::vector< KeyBinding > keys
Chords handled while focus is inside the node; the innermost node declaring a chord takes it....
Definition view.hpp:375
bool autofocus
Whether the widget takes keyboard focus when the content it is part of mounts; the first such widget ...
Definition view.hpp:378
LayoutHints layout
The size request; set once.
Definition view.hpp:354
std::function< void(Key)> onDrop
Set: the widget is a drop target, called with the dropped key.
Definition view.hpp:360
std::string surface
The named token set of the theme that styles this node and everything inside it; empty is the theme's...
Definition view.hpp:372
std::string testId
The stable identifier an automated test finds the widget by; empty means none. Set once.
Definition view.hpp:364
std::function< bool(Key const &)> accepts
Whether a drop of a key is accepted; empty accepts every key. Used only when onDrop is set.
Definition view.hpp:358
std::string focusId
The name a focus request after mount finds the widget by (Mounted::focus), such as a document element...
Definition view.hpp:367
Prop< bool > enabled
Whether the widget accepts input.
Definition view.hpp:352
Prop< bool > visible
Whether the widget is shown.
Definition view.hpp:350
One prop a loaded custom component is given.
Definition view.hpp:1144
std::variant< Prop< std::string >, Prop< bool >, Prop< double > > value
Its value: text for string, int, exact and json, which cross as text so that no value loses precision...
Definition view.hpp:1149
std::string name
The prop's name, as the component declares it.
Definition view.hpp:1146
A component the backend may load (spec 5 §10), with the built-in nodes shown when it does not.
Definition view.hpp:1156
int minVersion
The lowest version of a host component that will do.
Definition view.hpp:1164
Node fallback
The built-in nodes shown in its place; never null.
Definition view.hpp:1160
std::string name
The component's name, as the document's components declares it.
Definition view.hpp:1158
std::function< void(std::string event, std::string payload)> onEvent
Called with an event a loaded component raises: its name and its payload as the backend reports it.
Definition view.hpp:1166
std::vector< CustomProp > props
The props a loaded component is given, bound as any other property is.
Definition view.hpp:1162
A date, or a date and time, edited in a display zone and reported as an instant.
Definition view.hpp:914
int offsetMinutes
The display zone's offset from UTC, in minutes; set once.
Definition view.hpp:922
bool showLabel
Whether the input draws label as a visible caption itself; set once. A form leaves it false,...
Definition view.hpp:928
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:932
Prop< std::string > label
The field's label: its visible caption while showLabel; a form's cell draws it otherwise,...
Definition view.hpp:925
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:930
DateMode mode
Date or date and time; set once.
Definition view.hpp:920
std::function< void(std::optional< morph::time::Timestamp >)> onChange
Called with the instant the user entered, or nullopt when they cleared the field.
Definition view.hpp:918
Prop< std::optional< morph::time::Timestamp > > value
The instant shown; nullopt (or an empty Timestamp) shows an empty field.
Definition view.hpp:916
A modal overlay; its content is mounted only while it is open.
Definition view.hpp:694
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:704
Action onDismiss
Called when the user dismisses it (Esc on the TUI).
Definition view.hpp:702
Node child
The content; may be null.
Definition view.hpp:700
Prop< bool > open
Whether it is shown.
Definition view.hpp:696
Prop< std::string > title
The title.
Definition view.hpp:698
A panel that slides in from one edge over the content: a Dialog with a side, and the same rules.
Definition view.hpp:1072
Side side
The edge it slides in from; set once.
Definition view.hpp:1082
Node child
The content; may be null.
Definition view.hpp:1078
Prop< bool > open
Whether it is shown.
Definition view.hpp:1074
Action onDismiss
Called when the user dismisses it (Esc on the TUI).
Definition view.hpp:1080
Prop< std::string > title
The title.
Definition view.hpp:1076
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1084
A region that takes files dropped from the operating system.
Definition view.hpp:1128
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1140
std::vector< std::string > accept
The extensions it takes, such as ".csv", compared without regard to case; empty takes every file....
Definition view.hpp:1131
bool multiple
Whether a drop may carry more than one file; set once.
Definition view.hpp:1133
std::function< void(std::vector< std::string >)> onDrop
Called with the paths of a drop it took. A drop with a file outside accept, or with several files whe...
Definition view.hpp:1136
Node child
The content shown inside the region; may be null.
Definition view.hpp:1138
What a region shows when it has nothing to show: a title, an explanation and an optional action.
Definition view.hpp:1058
Prop< std::string > title
The title.
Definition view.hpp:1060
std::string icon
The name of an icon in the theme's icon map; empty shows none. Set once.
Definition view.hpp:1064
Prop< std::string > text
The explanation.
Definition view.hpp:1062
ActionButton action
The button below the text; an empty label shows none.
Definition view.hpp:1066
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1068
The state of a value a user edits or reads: whether it may be changed, must be given,...
Definition view.hpp:386
Prop< bool > required
Whether the field is marked as one that must be given. Only a mark: whether a value is missing is the...
Definition view.hpp:392
Prop< std::vector< std::string > > errors
The messages shown with the field, in order; empty shows none. Which errors show, and when,...
Definition view.hpp:395
Prop< bool > readonly
Whether the user may not change the value. A read-only field shows its value and can take focus and h...
Definition view.hpp:389
Prop< bool > stale
Whether the value shown is the last known one while a newer one is computed.
Definition view.hpp:397
A file path, typed or chosen in the platform's file dialog.
Definition view.hpp:956
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:964
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:966
std::function< void(std::string)> onPicked
Called with the path the user picked.
Definition view.hpp:962
Prop< std::string > path
The path shown.
Definition view.hpp:958
FilePickerMode mode
Open an existing file or name one to save; set once.
Definition view.hpp:960
One widget per row of a keyed collection, updated in place when a row with the same key changes.
Definition view.hpp:864
std::shared_ptr< detail::ForEachModel const > model
The rows, the key function and the row view, type-erased; null shows no rows.
Definition view.hpp:866
Axis axis
The direction rows are stacked in; set once.
Definition view.hpp:868
int gap
Space between rows: a spacing step, 0 to kMaxSpacingStep.
Definition view.hpp:870
Common common
Visibility, enablement, layout, drag-and-drop of the row container.
Definition view.hpp:872
One cell of a Grid.
Definition view.hpp:595
Node node
The cell's content; a null node leaves no cell.
Definition view.hpp:597
int span
How many columns it spans.
Definition view.hpp:599
Cells laid out row-major in a fixed number of columns.
Definition view.hpp:603
Align align
Where each cell's content sits vertically in its row; set once. A grid's columns share its width,...
Definition view.hpp:614
std::vector< GridCell > cells
The cells, row-major.
Definition view.hpp:607
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:611
int gap
Space between cells: a spacing step, 0 to kMaxSpacingStep.
Definition view.hpp:609
int columns
The column count.
Definition view.hpp:605
A keyboard chord a node handles while focus is inside it.
Definition view.hpp:339
Action onPress
Called when the chord is pressed.
Definition view.hpp:343
std::string chord
The chord: modifiers (Ctrl, Shift, Alt, Meta) joined to a key name, such as "Ctrl+S".
Definition view.hpp:341
One row of a KeyValue list.
Definition view.hpp:1038
bool operator==(KeyValueItem const &) const =default
Memberwise equality.
std::string label
The caption.
Definition view.hpp:1040
std::string value
The value, as text.
Definition view.hpp:1042
Captions beside their values, one pair per row.
Definition view.hpp:1050
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1054
Prop< std::vector< KeyValueItem > > items
The pairs, in order.
Definition view.hpp:1052
A widget's size request in both dimensions, and the bounds its extent is clamped into.
Definition view.hpp:308
Sizing height
Along the vertical axis.
Definition view.hpp:312
std::optional< int > minHeight
The least height, in backend units; nullopt for none.
Definition view.hpp:318
std::optional< int > minWidth
The least width, in backend units; nullopt for none.
Definition view.hpp:314
std::optional< int > maxWidth
The most width, in backend units; nullopt for none.
Definition view.hpp:316
std::optional< int > maxHeight
The most height, in backend units; nullopt for none.
Definition view.hpp:320
std::optional< Align > alignSelf
Where the node sits across its row's, column's or grid row's axis; nullopt takes the container's alig...
Definition view.hpp:323
bool operator==(LayoutHints const &) const =default
Memberwise equality.
Sizing width
Along the horizontal axis.
Definition view.hpp:310
One entry of a Menu: its label and its handler in one place, so there are no parallel lists.
Definition view.hpp:526
Prop< std::optional< bool > > checked
Engaged: the entry is checkable and shows this check mark; nullopt: not checkable....
Definition view.hpp:538
std::string keys
The chord shown beside the label, such as "Ctrl+S"; empty shows none. Set once. It only shows the cho...
Definition view.hpp:535
std::string icon
The name of an icon in the theme's icon map; empty shows none. Set once.
Definition view.hpp:532
std::vector< MenuItem > items
The submenu's entries; empty: the entry is chosen rather than opened.
Definition view.hpp:542
Action onSelect
Called when the entry is chosen.
Definition view.hpp:530
Prop< std::string > label
The caption.
Definition view.hpp:528
Prop< bool > enabled
Whether the entry can be chosen; a disabled entry's submenu cannot be opened.
Definition view.hpp:540
A vertical list of commands.
Definition view.hpp:546
std::vector< MenuItem > items
The entries, in order; the list itself is set once, each entry's label, check mark and enablement may...
Definition view.hpp:549
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:551
What a Node points at: exactly one node kind.
Definition view.hpp:1170
std::variant< Text, Button, TextInput, Checkbox, Select, Menu, Column, Row, Grid, Spacer, Panel, Scroll, Switch, Tabs, Dialog, Busy, ForEach, Table, DateTimeInput, Slider, FilePicker, Banner, Badge, Progress, Steps, KeyValue, EmptyState, Drawer, Splitter, Collapsible, DropZone, Custom, ActionBar > kind
The node.
Definition view.hpp:1175
A titled frame around one child; a collapsible one is an accordion section.
Definition view.hpp:624
Node child
The content; may be null.
Definition view.hpp:632
int padding
Space between frame and child: a spacing step, 0 to kMaxSpacingStep.
Definition view.hpp:630
Prop< std::string > title
The title.
Definition view.hpp:626
Prop< bool > collapsed
Whether it is collapsed; used only when collapsible.
Definition view.hpp:636
Node header
Shown beside the title, collapsed or not; may be null.
Definition view.hpp:628
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:640
bool collapsible
Whether the user can collapse it; set once.
Definition view.hpp:634
std::function< void(bool)> onToggle
Called with the collapsed state the user asked for; used only when collapsible.
Definition view.hpp:638
A progress bar.
Definition view.hpp:1006
Prop< std::string > label
A caption beside the bar.
Definition view.hpp:1010
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1012
Prop< std::optional< double > > value
How far along, from 0 to 1; a value outside is shown at the nearer end. nullopt: indeterminate.
Definition view.hpp:1008
Children laid out left to right.
Definition view.hpp:578
int gap
Space between children: a spacing step, 0 to kMaxSpacingStep.
Definition view.hpp:582
Justify justify
Where the spare width goes while no shown child stretches; set once.
Definition view.hpp:591
std::vector< Node > children
The children, in order; a null child is skipped.
Definition view.hpp:580
std::optional< Prop< std::optional< Tone > > > tone
A table row's tone: disengaged when none was given; an engaged Prop whose value is nullopt has no ton...
Definition view.hpp:587
Align align
Where the children sit across the row (vertically); set once.
Definition view.hpp:589
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:584
A scrollable viewport around one child.
Definition view.hpp:644
Axis axis
The scroll direction; set once.
Definition view.hpp:648
Node child
The content; may be null.
Definition view.hpp:646
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:650
One option of a Select.
Definition view.hpp:487
Key key
The option's identity.
Definition view.hpp:489
std::string label
The caption.
Definition view.hpp:491
bool operator==(SelectOption const &) const =default
Memberwise equality.
A choice of one option.
Definition view.hpp:499
Action onOpen
Called when the user opens the dropdown, whether or not it has options to show; a radio group is neve...
Definition view.hpp:508
Prop< std::optional< Key > > selected
The selected key, or none. A key outside options shows no selection.
Definition view.hpp:503
bool showLabel
Whether the input draws label as a visible caption itself; set once. A form leaves it false,...
Definition view.hpp:516
SelectStyle style
Dropdown or radio; set once.
Definition view.hpp:510
Prop< std::string > label
The field's label: its visible caption while showLabel; a form's cell draws it otherwise,...
Definition view.hpp:513
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:518
Prop< std::vector< SelectOption > > options
The options, in order.
Definition view.hpp:501
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:520
std::function< void(Key)> onSelect
Called with the key the user chose.
Definition view.hpp:505
How much space a widget asks for along one dimension, in backend units (a character cell on the TUI).
Definition view.hpp:270
static constexpr Sizing content() noexcept
Size to the content.
Definition view.hpp:284
bool operator==(Sizing const &) const =default
Memberwise equality.
static constexpr Sizing stretch(int weight=1) noexcept
A share of the leftover space.
Definition view.hpp:294
static constexpr Sizing fixed(int units) noexcept
A fixed size.
Definition view.hpp:289
Kind kind
The rule.
Definition view.hpp:278
int amount
Units for Fixed, weight for Stretch, unused for Content.
Definition view.hpp:280
Kind
The sizing rule.
Definition view.hpp:272
@ Fixed
Exactly amount units.
Definition view.hpp:274
@ Content
As much as the content needs.
Definition view.hpp:273
@ Stretch
A share of the leftover space, weighted by amount.
Definition view.hpp:275
An integer chosen on a range.
Definition view.hpp:936
Prop< std::int64_t > value
The value shown.
Definition view.hpp:938
std::function< void(std::int64_t)> onChange
Called with the value the user moved to.
Definition view.hpp:946
std::int64_t minimum
The smallest value; set once.
Definition view.hpp:940
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:952
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:950
std::function< void(std::int64_t)> onCommit
Called with the value a finished move ended on: a release after a drag, a key step,...
Definition view.hpp:948
std::int64_t step
The increment between neighbouring values; set once.
Definition view.hpp:944
std::int64_t maximum
The largest value; set once.
Definition view.hpp:942
A slot's read together with its id, as ui::slot makes it; a Prop made from it is a slot with that id.
Definition view.hpp:50
F read
Reads the slot's value; every signal it reads is a dependency.
Definition view.hpp:54
Empty space that takes what its layout hints ask for.
Definition view.hpp:618
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:620
Panes side by side, or stacked, with handles the user drags to share the space between them.
Definition view.hpp:1097
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1108
Axis orientation
The axis the panes are laid out along; set once.
Definition view.hpp:1101
std::vector< Node > children
The panes, in order; a null child is skipped.
Definition view.hpp:1099
Prop< std::vector< int > > sizes
Each pane's size, in backend units, in order; empty shares the space equally. A list whose length dif...
Definition view.hpp:1104
std::function< void(std::vector< int >)> onResize
Called with every pane's size after the user dragged a handle.
Definition view.hpp:1106
One step of a Steps indicator.
Definition view.hpp:1016
StepState state
Where it stands.
Definition view.hpp:1020
bool operator==(Step const &) const =default
Memberwise equality.
std::string label
The caption.
Definition view.hpp:1018
The steps of a process in order, each with its state.
Definition view.hpp:1028
Prop< std::vector< Step > > items
The steps.
Definition view.hpp:1030
Prop< std::optional< std::size_t > > current
The step to highlight, by position; nullopt or a position past the last highlights none.
Definition view.hpp:1032
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:1034
One case of a Switch.
Definition view.hpp:654
Key key
The selector value this case answers.
Definition view.hpp:656
Node node
The content; may be null.
Definition view.hpp:658
Shows the case whose key the selector yields; remounts only when that key changes.
Definition view.hpp:662
std::vector< SwitchCase > cases
The cases; the first one with the selected key wins.
Definition view.hpp:666
Prop< Key > selector
Which case to show.
Definition view.hpp:664
Node fallback
Shown when no case matches; may be null, and then nothing is shown.
Definition view.hpp:668
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:670
One page of Tabs.
Definition view.hpp:674
std::string label
The tab's caption; set once.
Definition view.hpp:676
Node node
The page content, mounted the first time the tab is selected.
Definition view.hpp:678
One column of a Table.
Definition view.hpp:876
Sizing width
The column's width request.
Definition view.hpp:880
TextRole role
The text style of this column's default cell; Normal renders unchanged. Has no effect on a column wit...
Definition view.hpp:883
std::string label
The header caption.
Definition view.hpp:878
Everything about a table beyond its columns and rows.
Definition view.hpp:1424
bool showHeader
Whether the header row is drawn.
Definition view.hpp:1426
Virtualization virtualize
The row count and margin above which a row's widget is realized only near the visible range.
Definition view.hpp:1428
Common common
Visibility, enablement, layout, drag-and-drop of the table.
Definition view.hpp:1438
SelectionMode selectionMode
How many rows the user can select.
Definition view.hpp:1430
std::function< void(std::vector< Key >)> onSelectionChange
Called with the keys the user selected.
Definition view.hpp:1434
Prop< std::vector< Key > > selection
The selected rows' keys.
Definition view.hpp:1432
std::function< void(Key)> onActivate
Called with a row's key when the user activates it.
Definition view.hpp:1436
What a table's row view builds for one row: its cells, one per column, and its tone.
Definition view.hpp:1442
std::optional< Prop< std::optional< Tone > > > tone
The row's tone, bound in the row's own scope; disengaged for none.
Definition view.hpp:1446
std::vector< Node > cells
The cells, one non-null node per column.
Definition view.hpp:1444
Keyed rows of cells under column headers. Each row's view is a Row whose children are the cells,...
Definition view.hpp:888
std::shared_ptr< detail::ForEachModel const > rows
The rows, the key function and the row view, type-erased; null shows no rows.
Definition view.hpp:892
SelectionMode selectionMode
How many rows the user can select; set once.
Definition view.hpp:902
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:910
bool showHeader
Whether the header row is drawn; set once. false suits several tables sharing one header,...
Definition view.hpp:895
std::function< void(Key)> onActivate
Called with a row's key when the user activates it (Enter or a double click).
Definition view.hpp:908
std::function< void(std::vector< Key >)> onSelectionChange
Called with the keys the user selected.
Definition view.hpp:906
Virtualization virtualize
The row count and margin above which a row's widget is realized only near the visible range; set once...
Definition view.hpp:900
std::vector< TableColumn > columns
The columns, in order; set once.
Definition view.hpp:890
Prop< std::vector< Key > > selection
The selected rows' keys. A key with no row selects nothing until such a row appears.
Definition view.hpp:904
A tab bar over pages.
Definition view.hpp:682
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:690
std::vector< Tab > tabs
The pages, in order; set once.
Definition view.hpp:684
Prop< std::size_t > selected
The selected page's index; an index past the last page shows none.
Definition view.hpp:686
std::function< void(std::size_t)> onSelect
Called with the index the user picked, and with the page still shown when a picked page fails to moun...
Definition view.hpp:688
An editable text field. The backend never reports its own setText back through onChange.
Definition view.hpp:437
Prop< std::string > unit
The unit the input shows beside its text, such as kg; empty shows none. Only a single-line or passwor...
Definition view.hpp:453
int decimals
The fraction digits a number is entered with, for a backend or a kit to read; -1 when the input does ...
Definition view.hpp:459
bool showLabel
Whether the input draws label as a visible caption itself; set once. A form leaves it false,...
Definition view.hpp:465
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:467
std::string icon
The name of an icon in the theme's icon set, drawn at the field's leading edge; empty shows none....
Definition view.hpp:456
std::function< void(std::string)> onSubmit
Called with the text when the user submits (Enter in single-line mode).
Definition view.hpp:443
TextInputMode mode
Single-line, multiline or password; set once.
Definition view.hpp:450
std::function< void(std::string)> onCommit
Called with the text when the user commits it: Enter in single-line mode, or focus leaving the field ...
Definition view.hpp:446
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:469
Prop< std::string > placeholder
Shown while the field is empty.
Definition view.hpp:448
std::function< void(std::string)> onChange
Called with the new text after every user edit.
Definition view.hpp:441
Prop< std::string > value
The text the field shows.
Definition view.hpp:439
Prop< std::string > label
The field's label: its visible caption while showLabel; a form's cell draws it otherwise,...
Definition view.hpp:462
Static or bound text.
Definition view.hpp:406
Common common
Visibility, enablement, layout, drag-and-drop.
Definition view.hpp:414
Prop< TextRole > role
The style.
Definition view.hpp:410
FieldState field
Read-only, required, errors and stale.
Definition view.hpp:412
Prop< std::string > text
The text, UTF-8.
Definition view.hpp:408
When a keyed row list realizes a widget only for the rows in or near the visible range,...
Definition view.hpp:854
std::size_t overscan
How many rows beyond the visible range, on each side, keep a realized widget so a small scroll does n...
Definition view.hpp:860
std::size_t threshold
Below this many rows, every row's widget is realized regardless of scroll position,...
Definition view.hpp:857
Whether a type is a SlotBinding: false here, true for the specialisation below.
Definition view.hpp:72
Tone
What a message or a mark conveys, mapped to the theme's colours.
Definition view.hpp:240
@ Info
Information.
Definition view.hpp:242
@ Warn
Something to check.
Definition view.hpp:244
@ Ok
Success.
Definition view.hpp:243
@ Err
A failure.
Definition view.hpp:245
@ Neutral
Nothing in particular.
Definition view.hpp:241
Node actionBar(ActionBar spec)
An action bar node.
Definition view.hpp:1324
Node panel(Panel spec)
A titled frame, optionally collapsible.
Definition view.hpp:1244
Node collapsible(Collapsible spec)
A titled section the user opens and closes.
Definition view.hpp:1334
TextInputMode
How a TextInput edits.
Definition view.hpp:209
@ SingleLine
One line; Enter submits.
Definition view.hpp:210
@ Password
One line, masked.
Definition view.hpp:212
Node keyValue(KeyValue spec)
Captions beside their values.
Definition view.hpp:1309
TextRole
How a Text is styled; each backend maps a role to its theme.
Definition view.hpp:199
@ Heading
A section heading.
Definition view.hpp:202
@ Muted
Secondary text.
Definition view.hpp:201
@ Normal
Body text.
Definition view.hpp:200
DateMode
What a DateTimeInput edits.
Definition view.hpp:228
@ DateTime
A date and a time of day.
Definition view.hpp:230
Node grid(Grid spec)
Cells in a fixed number of columns.
Definition view.hpp:1234
Node forEach(std::function< std::vector< RowT >()> rows, std::function< Key(RowT const &)> keyOf, std::function< Node(reactive::Signal< RowT > const &)> rowView, Axis axis=Axis::Vertical, int gap=0)
One widget per row of a keyed collection read through a binding (a Query's value() included).
Definition view.hpp:1396
Node slider(Slider spec)
An integer on a range.
Definition view.hpp:1279
Node badge(Badge spec)
A short label marking a state.
Definition view.hpp:1294
std::function< void()> Action
A widget event handler taking no arguments.
Definition view.hpp:33
Node banner(Banner spec)
A message across the top of a region.
Definition view.hpp:1289
Node textInput(TextInput spec)
An editable text field.
Definition view.hpp:1204
Node column(Column spec)
Children stacked top to bottom.
Definition view.hpp:1224
Node dateTimeInput(DateTimeInput spec)
A date or date-and-time field.
Definition view.hpp:1274
Node custom(Custom spec)
A component loaded by name, mounted by Mounted as its fallback.
Definition view.hpp:1346
Node dropZone(DropZone spec)
A region that takes dropped files.
Definition view.hpp:1339
Node busy(Busy spec)
A progress indicator.
Definition view.hpp:1269
constexpr int kMaxSpacingStep
The largest spacing step: a gap or padding names a step from 0 (none) to this, which each backend map...
Definition view.hpp:556
SelectionMode
How many rows of a Table the user can select.
Definition view.hpp:263
@ None
Rows can be activated, not selected.
Definition view.hpp:264
Node menu(Menu spec)
A list of commands.
Definition view.hpp:1219
Node switchOn(std::function< E()> selector, std::vector< std::pair< E, Node > > cases, Node fallback={})
A Switch over an enumeration: each enumerator becomes the int64 key of its case.
Definition view.hpp:1365
Node filePicker(FilePicker spec)
A file path field.
Definition view.hpp:1284
Node checkbox(Checkbox spec)
A labelled check box.
Definition view.hpp:1209
Node emptyState(EmptyState spec)
What a region shows when it has nothing to show.
Definition view.hpp:1314
Node spacer(Spacer spec={})
Empty space.
Definition view.hpp:1239
Side
The edge a Drawer slides in from, in reading direction.
Definition view.hpp:249
@ End
The right edge in a left-to-right layout.
Definition view.hpp:251
FilePickerMode
Whether a FilePicker chooses an existing file or a file to write.
Definition view.hpp:234
@ Open
An existing file.
Definition view.hpp:235
Node switchOf(Switch spec)
Shows the case the selector picks. Spelled switchOf because switch is a keyword.
Definition view.hpp:1254
Node text(Text spec)
Static or bound text.
Definition view.hpp:1194
Node select(Select spec)
A choice of one option.
Definition view.hpp:1214
SelectStyle
How a Select presents its options.
Definition view.hpp:216
@ Radio
Every option visible, one marked.
Definition view.hpp:218
@ Dropdown
A closed field that opens a list.
Definition view.hpp:217
Node splitter(Splitter spec)
Panes with handles the user drags.
Definition view.hpp:1329
constexpr int spacingStep(int value) noexcept
A gap or padding as the spacing step a backend receives.
Definition view.hpp:561
Node progress(Progress spec)
A progress bar.
Definition view.hpp:1299
std::shared_ptr< NodeData const > Node
A view-tree node: immutable, shared data.
Definition view.hpp:403
Node drawer(Drawer spec)
A panel that slides in from one edge.
Definition view.hpp:1319
Node row(Row spec)
Children laid out left to right.
Definition view.hpp:1229
std::variant< std::int64_t, std::string > Key
The identity of a Switch case, a Select option, a ForEach or Table row and a drag payload.
Definition view.hpp:38
Node table(std::vector< TableColumn > columns, std::function< std::vector< RowT >()> rows, std::function< Key(RowT const &)> keyOf, std::function< std::vector< Node >(reactive::Signal< RowT > const &)> cells, TableOptions options={})
A table of keyed rows: one Row of cells per row, under the column headers.
Definition view.hpp:1464
Node steps(Steps spec)
The steps of a process.
Definition view.hpp:1304
Node tabs(Tabs spec)
A tab bar over pages.
Definition view.hpp:1259
Axis
The direction a stack arranges, or a scroll area scrolls, its content.
Definition view.hpp:222
@ Vertical
Top to bottom.
Definition view.hpp:223
@ Horizontal
Left to right.
Definition view.hpp:224
Node dialog(Dialog spec)
A modal overlay.
Definition view.hpp:1264
std::uint32_t SlotId
The stable identity of a slot: a bound property a renderer can refer to by number.
Definition view.hpp:45
Node scroll(Scroll spec)
A scrollable viewport.
Definition view.hpp:1249
StepState
Where one step of a Steps indicator stands.
Definition view.hpp:255
@ Pending
Not started.
Definition view.hpp:256
Node makeNode(Kind spec)
Wraps one node aggregate into a shared node.
Definition view.hpp:1185
Node button(Button spec)
A push button.
Definition view.hpp:1199
SlotBinding< std::decay_t< F > > slot(SlotId slotId, F &&read)
A slot with an id.
Definition view.hpp:63