23#include <glaze/glaze.hpp>
29#include <unordered_map>
32#include "../attributes.hpp"
33#include "../core/bridge.hpp"
34#include "../core/callback_scope.hpp"
35#include "../core/logger.hpp"
38namespace morph::forms {
51 using binds = std::tuple<Binds...>;
55 [[nodiscard]]
static constexpr std::string_view
title() noexcept {
return Title.view(); }
68 [[nodiscard]]
static constexpr std::string_view
title() noexcept {
return Title.view(); }
97 glz::generic_u64 dom{};
99 dom[
"s-title"] = std::string{G::title()};
101 glz::generic_u64::array_t sections{};
102 detail::forEachTupleElement<typename G::sections>([&]<
typename SectionT, std::size_t I>() {
103 static_cast<void>(I);
104 glz::generic_u64 entry{};
106 entry[
"title"] = std::string{SectionT::title()};
107 if constexpr (std::tuple_size_v<typename SectionT::binds> != 0) {
108 detail::emitBindsInto<typename SectionT::binds>(entry[
"prefill"]);
110 sections.emplace_back(std::move(entry));
112 dom[
"s-sections"] = sections;
114 return glz::write_json(dom).value_or(std::string{});
132template <
typename Model,
typename... Sections>
134 static_assert(
sizeof...(Sections) > 0,
"SectionSet: a group needs at least one section");
135 static_assert(::morph::forms::detail::AllDistinct<
typename Sections::action...>::value,
136 "SectionSet: section action types must be pairwise distinct");
150 std::function<
void(std::exception_ptr)> onError MORPH_LIFETIMEBOUND =
nullptr)
151 : _handler{handler}, _onError{std::move(onError)} {}
176 note(
"SectionSet::~SectionSet");
177 _callbacks.requestStop();
187 template <auto FieldPtr>
188 void set(::morph::bridge::detail::MemberPointerTraits<
decltype(FieldPtr)>::ValueType value) {
189 using A = ::morph::bridge::detail::MemberPointerTraits<
decltype(FieldPtr)>::ClassType;
190 static_assert((std::is_same_v<A, typename Sections::action> || ...),
191 "SectionSet::set<>: the field's action is not a section of this group");
192 note(
"SectionSet::set");
193 std::get<A>(_drafts).*FieldPtr = std::move(value);
195 fire<A>(std::get<A>(_drafts));
205 template <
typename A>
207 static_assert((std::is_same_v<A, typename Sections::action> || ...),
208 "SectionSet::reset<>: not a section of this group");
209 note(
"SectionSet::reset");
210 std::get<A>(_drafts) = A{};
218 template <
typename A>
220 static_assert((std::is_same_v<A, typename Sections::action> || ...),
221 "SectionSet::draft<>: not a section of this group");
222 note(
"SectionSet::draft");
223 return std::get<A>(_drafts);
231 [[nodiscard]] std::optional<std::string>
resolved(std::string_view path)
const {
232 note(
"SectionSet::resolved");
233 auto iter = _resolvedValues.find(std::string{path});
234 if (iter == _resolvedValues.end()) {
249 template <
typename A>
250 void captureResult(const ::morph::model::ActionTraits<A>::Result& result) {
251 note(
"SectionSet::captureResult");
253 auto record = [&](
const auto& value) {
254 ::morph::forms::detail::forEachNamedMember(
255 value, [&]<std::size_t I>(std::string_view name,
const auto& member) {
256 static_cast<void>(I);
264 if (!glz::write_json(member, json)) {
265 _resolvedValues[std::string{typeId} +
"." + std::string{name}] = std::move(json);
269 record(std::get<A>(_drafts));
276 static void logUnhandledError(std::string_view typeId,
const std::exception_ptr& err) {
278 std::rethrow_exception(err);
279 }
catch (
const std::exception& exc) {
280 ::morph::log::logError(std::string{
"[section:"} + std::string{typeId} +
281 "] unhandled exception: " + exc.what());
283 ::morph::log::logError(std::string{
"[section:"} + std::string{typeId} +
"] unhandled unknown exception");
299 template <
typename A>
301 _handler.execute(std::move(
draft))
303 [
this](const ::morph::model::ActionTraits<A>::Result& result) {
304 this->
template captureResult<A>(result);
306 .onError(_callbacks, [
this](
const std::exception_ptr& err) {
310 logUnhandledError(::morph::model::ActionTraits<A>::typeId(), err);
317 void note(
char const* site)
const noexcept {
318 ::morph::exec::detail::noteOwner(site, _handler.owner().coreExecutor(), _handler.onOwner());
321 ::morph::bridge::BridgeHandler<Model>& _handler;
322 std::function<void(std::exception_ptr)> _onError;
326 std::tuple<
typename Sections::action...> _drafts{};
327 std::unordered_map<std::string, std::string> _resolvedValues;
330 ::morph::async::CallbackScope _callbacks;
338#define BRIDGE_REGISTER_SECTION_GROUP(G, NAME) \
340 struct morph::forms::SectionGroupTraits<G> { \
341 static constexpr std::string_view typeId() noexcept { return NAME; } \
::morph::detail::FixedString< N > FixedString
A structural, NTTP-capable compile-time string (for naming the options action and its value/label res...
RAII wrapper that binds a single model type to a Bridge.
std::string sectionGroupSchemaJson()
Generates the s-* JSON document for section group G.
Machinery shared by the two form session types: morph::flows::FlowSession (ordered wizard steps) and ...
Traits specialisation that maps an action type to its id, JSON codec, and result type.
static constexpr bool ready(const Action &action)
Returns true if the action is in a state that should be executed.
std::function< void()> Action
A widget event handler taking no arguments.