forms(qml): a host slot can draw a std::vector<Row> member, and the form encodes its rows - #809
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This was referenced Sep 24, 2026
Yaraslaut
added a commit
that referenced
this pull request
Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817) Every prefill path wrote *control text*: CollectionView sets a `field_<name>` TextField, WizardView calls setFieldValue with text. An editing flow -- open a saved sample, read its section DTO, prefill the form, edit, submit -- had to turn each wire value into the text its control holds by hand: a {num,den,dp} into locale digits at the declared precision, an ISO instant into the display zone, a closed-set value into its valueJson, and every row of a std::vector<Row> into cell texts, the inverse of the encoder #809 added. `decodeFieldValue(field, value)` is that inverse, kind by kind, on exact digits (a Quantity's num/den are divided as digit strings, an int64 id from JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a payload object, resets unit selectors to the canonical unit, bumps `prefillRevision` so every drawn control re-seeds (a fetched Choice also re-selects when its options arrive -- before this it showed "— select —" for a retained value), re-fetches dependent Choices, and revalidates *inside* the programmaticEdit window. That last point is measured, not assumed: the first version reused withoutAutoSubmit, whose final revalidate runs after the suppression is lifted, and the round-trip case caught the ready prefilled form submitting itself. `prefillFromJson(text)` parses with JsonExact. tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against previewLine for every member kind, the drawn controls and slots holding the values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a fetched Choice with an id past 2^53, and no submission. Removing the re-seed reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348 passed on MSVC 14.51 / Qt 6.11.1. Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes into). Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut
added a commit
that referenced
this pull request
Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817) Every prefill path wrote *control text*: CollectionView sets a `field_<name>` TextField, WizardView calls setFieldValue with text. An editing flow -- open a saved sample, read its section DTO, prefill the form, edit, submit -- had to turn each wire value into the text its control holds by hand: a {num,den,dp} into locale digits at the declared precision, an ISO instant into the display zone, a closed-set value into its valueJson, and every row of a std::vector<Row> into cell texts, the inverse of the encoder #809 added. `decodeFieldValue(field, value)` is that inverse, kind by kind, on exact digits (a Quantity's num/den are divided as digit strings, an int64 id from JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a payload object, resets unit selectors to the canonical unit, bumps `prefillRevision` so every drawn control re-seeds (a fetched Choice also re-selects when its options arrive -- before this it showed "— select —" for a retained value), re-fetches dependent Choices, and revalidates *inside* the programmaticEdit window. That last point is measured, not assumed: the first version reused withoutAutoSubmit, whose final revalidate runs after the suppression is lifted, and the round-trip case caught the ready prefilled form submitting itself. `prefillFromJson(text)` parses with JsonExact. tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against previewLine for every member kind, the drawn controls and slots holding the values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a fetched Choice with an id past 2^53, and no submission. Removing the re-seed reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348 passed on MSVC 14.51 / Qt 6.11.1. Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes into). Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut
force-pushed
the
feature/forms-object-array-slot
branch
from
September 25, 2026 06:56
2b1a564 to
8fcfad4
Compare
Yaraslaut
added a commit
that referenced
this pull request
Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817) Every prefill path wrote *control text*: CollectionView sets a `field_<name>` TextField, WizardView calls setFieldValue with text. An editing flow -- open a saved sample, read its section DTO, prefill the form, edit, submit -- had to turn each wire value into the text its control holds by hand: a {num,den,dp} into locale digits at the declared precision, an ISO instant into the display zone, a closed-set value into its valueJson, and every row of a std::vector<Row> into cell texts, the inverse of the encoder #809 added. `decodeFieldValue(field, value)` is that inverse, kind by kind, on exact digits (a Quantity's num/den are divided as digit strings, an int64 id from JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a payload object, resets unit selectors to the canonical unit, bumps `prefillRevision` so every drawn control re-seeds (a fetched Choice also re-selects when its options arrive -- before this it showed "— select —" for a retained value), re-fetches dependent Choices, and revalidates *inside* the programmaticEdit window. That last point is measured, not assumed: the first version reused withoutAutoSubmit, whose final revalidate runs after the suppression is lifted, and the round-trip case caught the ready prefilled form submitting itself. `prefillFromJson(text)` parses with JsonExact. tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against previewLine for every member kind, the drawn controls and slots holding the values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a fetched Choice with an id past 2^53, and no submission. Removing the re-seed reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348 passed on MSVC 14.51 / Qt 6.11.1. Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes into). Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut
force-pushed
the
feature/forms-object-array-slot
branch
from
September 25, 2026 07:54
8fcfad4 to
903e512
Compare
…orm encodes its rows
A collection of objects has no built-in control: the comma-separated array
control encodes strings, so DynamicForm reports the member unrepresentable and
a form that must carry it never becomes ready. That was right for the built-in
controls, and it left a host with nowhere to go -- registering a grid slot for
the member changed nothing, because the slot could only hand `setValue` a text
the array encoder then split on commas, and the slot was never told what the
row type's members are.
When a registered slot claims a top-level `std::vector<Row>` member:
- `fields` describes the row type with the same descriptor builder the
action uses (`describeObject`, depth-limited to one level so a
self-referential row type cannot loop) and puts it on the field as
`itemFields`: label, unit, decimals, readOnly, required and the kind
flags, in x-order order -- the grid's columns. `isObjectArray` and
`claimedBySlot` say which case the descriptor is.
- `unrepresentable` is "" for it, because an encoding now exists.
- The slot writes rows as `{member: cellText}` objects, where a cell holds
what that member's built-in control would hold. `encodeObjectArray`
encodes every cell with `encodeFieldText` -- `fieldJsonLiteral`'s body,
lifted to take an explicit text and unit index -- so a `Quantity` cell is
an exact {num,den,dp}, an over-precise one is refused rather than rounded,
a blank optional cell is omitted and a blank required one keeps the form
unready.
Slots also gain four optional members, each assigned only when the slot
declares it, so every existing slot is untouched: `fieldText` (a binding to the
retained text -- until now a slot never learned a value it had not written
itself, so prefill, `resetFields()` and a tab rebuild all left it stale),
`rows`, `setRows(rows)` and `form`. With `fieldText`, CollectionView's row
prefill reaches a slot-overridden scalar field too, which views.md listed as a
limitation.
Nothing changes without a slot: tst_DynamicFormNestedAggregate.qml is
untouched and green. The new tst_DynamicFormObjectArraySlot.qml (18 cases)
asserts submitted bodies; replacing the cell encoder with plain string quoting
reddens 3 of them. forms_qml_logic: 335 passed on MSVC 14.51 / Qt 6.11.1.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…record into DynamicForm for editing (fixes #814) (#817) Every prefill path wrote *control text*: CollectionView sets a `field_<name>` TextField, WizardView calls setFieldValue with text. An editing flow -- open a saved sample, read its section DTO, prefill the form, edit, submit -- had to turn each wire value into the text its control holds by hand: a {num,den,dp} into locale digits at the declared precision, an ISO instant into the display zone, a closed-set value into its valueJson, and every row of a std::vector<Row> into cell texts, the inverse of the encoder #809 added. `decodeFieldValue(field, value)` is that inverse, kind by kind, on exact digits (a Quantity's num/den are divided as digit strings, an int64 id from JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a payload object, resets unit selectors to the canonical unit, bumps `prefillRevision` so every drawn control re-seeds (a fetched Choice also re-selects when its options arrive -- before this it showed "— select —" for a retained value), re-fetches dependent Choices, and revalidates *inside* the programmaticEdit window. That last point is measured, not assumed: the first version reused withoutAutoSubmit, whose final revalidate runs after the suppression is lifted, and the round-trip case caught the ready prefilled form submitting itself. `prefillFromJson(text)` parses with JsonExact. tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against previewLine for every member kind, the drawn controls and slots holding the values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a fetched Choice with an id past 2^53, and no submission. Removing the re-seed reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348 passed on MSVC 14.51 / Qt 6.11.1. Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes into). Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…on too
slotClaims() is the delegate's own slot resolution, but it passed five
arguments while the delegate passes the kind as well, so a grid registered
with SlotRegistry.byKind("objectArray", ...) was drawn while the form kept
the collection unrepresentable and never encoded its rows. The kind is now
computed once per descriptor and handed to both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4eif7wQNNhSkHUKYqq5Xq
The helper now revalidates inside its suppression window, so the hand-rolled copy in prefill() and its comment explaining why it avoided the helper no longer describe anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4eif7wQNNhSkHUKYqq5Xq
Yaraslaut
force-pushed
the
feature/forms-object-array-slot
branch
from
September 25, 2026 08:49
903e512 to
9685964
Compare
Yaraslaut
added a commit
that referenced
this pull request
Sep 25, 2026
… encodes it member by member (#822) A struct member -- `IgnitionSpecimen specimen`, or `std::optional<PycnometerDetermination> determination1` -- has no built-in control: DynamicForm flattens it to one text field, reports it unrepresentable, and a form that must carry it never becomes ready. #809 gave a host slot a way out for `std::vector<Row>`; a nested object had none. When a registered slot claims a top-level object member: - `fields` marks it `isObject` and describes its members as `objectFields` with the same descriptor builder (label, unit, decimals, readOnly, required, kind flags). The description recurses: a member that is itself an object carries its own `objectFields`, a collection its `itemFields`. It is bounded by `maxObjectDepth` (4) and stops at the first repetition of a `$defs` type on the path, so a self-referential type cannot loop. - `unrepresentable` is "" for it and for every object or collection described inside it. - The slot writes `{member: cellText | nestedValue}` objects through the new optional `setObject(value)` and reads them back through the `objectValue` binding. `encodeObjectValue` encodes each leaf with `encodeFieldText` (locale, precision, bounds), a nested object recursively and a collection with the row encoder. A blank optional leaf or object is omitted; a blank required leaf or object keeps the form unready. - `prefill` / `prefillFromJson` decode a stored object back into that shape (`decodeObjectValue`), so prefill -> submit round-trips. An object member no slot claims behaves as before; tst_DynamicFormNestedAggregate.qml is untouched and green. Schema side: `recurseIntoNestedAggregateIfAny` did not see through `std::optional<Sub>`, which glaze spells as an `anyOf` over Sub's schema and null, so an optional sub-record's members had no x-order, title, required or FieldMeta (unit, x-displayDecimals, label). It now recurses into the non-null branch. Three new test_nested_forms.cpp cases pin it; without the fix two of them fail. The new tst_DynamicFormObjectSlot.qml (24 cases) asserts submitted bodies for both consumer shapes; replacing the leaf encoder with plain string quoting reddens 9 of them. forms_qml_logic: 419 passed, full ctest 1596/1596 on MSVC / Qt 6.11.1. Co-authored-by: Yaraslau Tamashevich <y.tamashevich@lastrada.net> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
std::vector<Row>member has no built-in control, so DynamicForm reports it unrepresentable and the form never becomes ready. A host grid slot could not fix that:setValuetext went through the comma-splitting array encoder, and the slot was never told the row type's members.When a registered slot claims a top-level collection of objects:
isObjectArrayitemsresolve to an object schema withpropertiesitemFieldsx-orderorder (label, unit, decimals, readOnly, required, kind flags) — the grid's columns; one level only, so a self-referential row cannot loopclaimedBySlotunrepresentableis then""{member: cellText}(setRows(rows)orsetValue(JSON)); each cell is encoded withencodeFieldText(fieldJsonLiteral's body, lifted to take an explicit text/unit index), so aQuantitycell is exact{num,den,dp}, an over-precise one is refused, a blank required cell keeps the form unready.[]is a valid value.fieldText(binding to the retained text — prefill,resetFields()and tab rebuilds now reach a slot),rows,setRows,form. WithfieldText, CollectionView's row prefill reaches a slot-overridden scalar field (views.md limitation updated).tst_DynamicFormNestedAggregate.qmluntouched and green.x-i18nKeyor the literal only.Spec:
docs/spec/forms/forms.md— new "Collections of objects — a host slot draws them", slot-contract table in "Theming / component-override registry", "Whatreadyclaims".Tests (MSVC 14.51, Qt 6.11.1): new
tst_DynamicFormObjectArraySlot.qml(18 cases, asserting submitted bodies); replacing the cell encoder with string quoting reddens 3.forms_qml_logic335 passed.🤖 Generated with Claude Code