morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
overrides.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5/// @file forms/engine/overrides.hpp
6/// @brief `Overrides`: an application's replacements for the control `formView` draws for a field, chosen by
7/// field path, widget hint, unit or kind; `FieldView`, what a replacement is handed.
8///
9/// Specified in `docs/spec/forms/engine.md`, "Rendering".
10
11#include <functional>
12#include <map>
13#include <memory>
14#include <string>
15#include <string_view>
16#include <utility>
17
18#include "../../ui/view.hpp"
19#include "field_model.hpp"
20#include "form_session.hpp"
21
22namespace morph::forms {
23
24struct FormViewOptions;
25
26/// @brief A field's spec and state, handed to an override: what it shows (the value, the draft, the errors, the
27/// options) and what it writes (the draft). Copyable; valid while the session lives.
28class FieldView {
29public:
30 /// @param session The session. Borrowed.
31 /// @param state The field. Borrowed.
32 /// @param options The view options the form is drawn with.
33 FieldView(FormSession& session, FieldState& state, std::shared_ptr<FormViewOptions const> options) noexcept
34 : _session{&session}, _state{&state}, _options{std::move(options)} {}
35
36 /// @brief The field's spec.
37 /// @return The spec.
38 [[nodiscard]] FieldSpec const& spec() const noexcept { return _state->spec(); }
39
40 /// @brief The field's state: draft, encoding, error, visibility, options. Writing its `text()` is a user edit.
41 /// @return The state.
42 [[nodiscard]] FieldState& state() const noexcept { return *_state; }
43
44 /// @brief The session the field belongs to.
45 /// @return The session.
46 [[nodiscard]] FormSession& session() const noexcept { return *_session; }
47
48 /// @brief The control `formView` draws for this field without overrides, for an override to wrap.
49 /// @return The control.
50 [[nodiscard]] ui::Node defaultControl() const;
51
52private:
53 FormSession* _session;
54 FieldState* _state;
55 std::shared_ptr<FormViewOptions const> _options;
56};
57
58/// @brief Replacement controls, resolved per field in the order field path, widget hint, unit, kind, else the
59/// built-in control.
60///
61/// An override replaces the control only: the cell around it (label, help, visibility) is still drawn.
62class Overrides {
63public:
64 /// @brief Draws a field's control.
65 using Render = std::function<ui::Node(FieldView const&)>;
66
67 /// @brief Overrides one field.
68 /// @param path The field's spec path (`FieldSpec::path`: `name`, `parent.member`, `rows[].member`).
69 /// @param render The control.
70 /// @return `*this`, for chaining.
71 Overrides& byField(std::string path, Render render) {
72 _byField.insert_or_assign(std::move(path), std::move(render));
73 return *this;
74 }
75
76 /// @brief Overrides every field with an `x-widget` hint.
77 /// @param hint The hint, e.g. `"slider"`.
78 /// @param render The control.
79 /// @return `*this`, for chaining.
80 Overrides& byWidget(std::string hint, Render render) {
81 _byWidget.insert_or_assign(std::move(hint), std::move(render));
82 return *this;
83 }
84
85 /// @brief Overrides every field in a unit.
86 /// @param unit The unit's `ExtUnits.unitAscii`.
87 /// @param render The control.
88 /// @return `*this`, for chaining.
89 Overrides& byUnit(std::string unit, Render render) {
90 _byUnit.insert_or_assign(std::move(unit), std::move(render));
91 return *this;
92 }
93
94 /// @brief Overrides every field of a kind.
95 /// @param kind The kind.
96 /// @param render The control.
97 /// @return `*this`, for chaining.
99 _byKind.insert_or_assign(kind, std::move(render));
100 return *this;
101 }
102
103 /// @brief The override for @p spec, if any.
104 /// @param spec The field.
105 /// @return The first match in field, widget, unit, kind order; null when none matches.
106 [[nodiscard]] Render const* resolve(FieldSpec const& spec) const {
107 if (auto const found = _byField.find(spec.path); found != _byField.end()) {
108 return &found->second;
109 }
110 if (!spec.widget.empty()) {
111 if (auto const found = _byWidget.find(spec.widget); found != _byWidget.end()) {
112 return &found->second;
113 }
114 }
115 if (!spec.unitAscii.empty()) {
116 if (auto const found = _byUnit.find(spec.unitAscii); found != _byUnit.end()) {
117 return &found->second;
118 }
119 }
120 if (auto const found = _byKind.find(spec.kind); found != _byKind.end()) {
121 return &found->second;
122 }
123 return nullptr;
124 }
125
126private:
127 std::map<std::string, Render, std::less<>> _byField;
128 std::map<std::string, Render, std::less<>> _byWidget;
129 std::map<std::string, Render, std::less<>> _byUnit;
130 std::map<FieldKind, Render> _byKind;
131};
132
133} // namespace morph::forms
The reactive state of one field: its draft and everything derived from it.
A field's spec and state, handed to an override: what it shows (the value, the draft,...
Definition overrides.hpp:28
FieldView(FormSession &session, FieldState &state, std::shared_ptr< FormViewOptions const > options) noexcept
Definition overrides.hpp:33
FormSession & session() const noexcept
The session the field belongs to.
Definition overrides.hpp:46
FieldSpec const & spec() const noexcept
The field's spec.
Definition overrides.hpp:38
ui::Node defaultControl() const
The control formView draws for this field without overrides, for an override to wrap.
FieldState & state() const noexcept
The field's state: draft, encoding, error, visibility, options. Writing its text() is a user edit.
Definition overrides.hpp:42
The reactive state of one form: a controller over a FormModel.
Replacement controls, resolved per field in the order field path, widget hint, unit,...
Definition overrides.hpp:62
Overrides & byUnit(std::string unit, Render render)
Overrides every field in a unit.
Definition overrides.hpp:89
Overrides & byWidget(std::string hint, Render render)
Overrides every field with an x-widget hint.
Definition overrides.hpp:80
std::function< ui::Node(FieldView const &)> Render
Draws a field's control.
Definition overrides.hpp:65
Overrides & byKind(FieldKind kind, Render render)
Overrides every field of a kind.
Definition overrides.hpp:98
Render const * resolve(FieldSpec const &spec) const
The override for spec, if any.
Overrides & byField(std::string path, Render render)
Overrides one field.
Definition overrides.hpp:71
FormModel: the immutable runtime model of one action's form, read from the schema JSON schemaJson<A>(...
FieldKind
The control a field gets, which the JSON type alone does not say.
FormSession: the reactive state of one form over a FormModel — drafts, encoded values,...
Everything the engine knows about one field, from its schema property.
std::string unitAscii
ExtUnits.unitAscii.
std::string path
The path from the action: name, parent.name, or rows[].name for a collection's member.
std::string widget
x-widget, empty when none.
FieldKind kind
The control and encoder.
How formView lays a form out and labels its chrome.
Definition form_view.hpp:38
The toolkit-agnostic view tree: props, node kinds and their builders.
std::shared_ptr< NodeData const > Node
A view-tree node: immutable, shared data.
Definition view.hpp:403