Skip to content

Commit 768c101

Browse files
committed
Implement type-dependent UI
1 parent fa353f5 commit 768c101

4 files changed

Lines changed: 412 additions & 10 deletions

File tree

src/customwidgets/schemaformwidget.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <QSpinBox>
1010
#include <climits>
1111
#include <limits>
12+
#include <utility>
1213

1314
SchemaFormWidget::SchemaFormWidget(QWidget* parent) : QWidget(parent), _pFormLayout(new QFormLayout(this))
1415
{
@@ -18,6 +19,11 @@ SchemaFormWidget::SchemaFormWidget(QWidget* parent) : QWidget(parent), _pFormLay
1819
void SchemaFormWidget::setSchema(const QJsonObject& schema, const QJsonObject& values)
1920
{
2021
_fields.clear();
22+
_conditionalTriggerKey.clear();
23+
_conditionalTriggerConst.clear();
24+
_thenKeys.clear();
25+
_elseKeys.clear();
26+
_currentTriggerValue.clear();
2127

2228
// removeRow() deletes both the label and field widget
2329
while (_pFormLayout->rowCount() > 0)
@@ -72,6 +78,46 @@ void SchemaFormWidget::setSchema(const QJsonObject& schema, const QJsonObject& v
7278
_fields.append({ key, widget });
7379
_pFormLayout->addRow(label + ":", widget);
7480
}
81+
82+
if (!parseConditional(schema))
83+
{
84+
return;
85+
}
86+
87+
const QJsonObject thenProps = schema.value("then").toObject().value("properties").toObject();
88+
for (const QString& key : std::as_const(_thenKeys))
89+
{
90+
QJsonObject propSchema = thenProps.value(key).toObject();
91+
QString label = propSchema.value("title").toString(key);
92+
QWidget* widget = createWidgetForProperty(propSchema, values.value(key));
93+
_fields.append({ key, widget });
94+
_pFormLayout->addRow(label + ":", widget);
95+
}
96+
97+
const QJsonObject elseProps = schema.value("else").toObject().value("properties").toObject();
98+
for (const QString& key : std::as_const(_elseKeys))
99+
{
100+
QJsonObject propSchema = elseProps.value(key).toObject();
101+
QString label = propSchema.value("title").toString(key);
102+
QWidget* widget = createWidgetForProperty(propSchema, values.value(key));
103+
_fields.append({ key, widget });
104+
_pFormLayout->addRow(label + ":", widget);
105+
}
106+
107+
applyConditional(values.value(_conditionalTriggerKey).toString());
108+
109+
for (const auto& [key, widget] : std::as_const(_fields))
110+
{
111+
if (key == _conditionalTriggerKey)
112+
{
113+
auto* combo = qobject_cast<QComboBox*>(widget);
114+
if (combo != nullptr)
115+
{
116+
connect(combo, &QComboBox::currentIndexChanged, this, &SchemaFormWidget::onTriggerChanged);
117+
}
118+
break;
119+
}
120+
}
75121
}
76122

77123
QWidget* SchemaFormWidget::createWidgetForProperty(const QJsonObject& propSchema, const QJsonValue& value)
@@ -155,11 +201,110 @@ QWidget* SchemaFormWidget::createWidgetForProperty(const QJsonObject& propSchema
155201
}
156202
}
157203

204+
bool SchemaFormWidget::parseConditional(const QJsonObject& schema)
205+
{
206+
const QJsonObject ifObj = schema.value("if").toObject();
207+
const QJsonObject thenObj = schema.value("then").toObject();
208+
const QJsonObject elseObj = schema.value("else").toObject();
209+
210+
if (ifObj.isEmpty() || thenObj.isEmpty() || elseObj.isEmpty())
211+
{
212+
return false;
213+
}
214+
215+
const QJsonArray required = ifObj.value("required").toArray();
216+
if (required.size() != 1)
217+
{
218+
return false;
219+
}
220+
221+
const QString triggerKey = required.at(0).toString();
222+
if (triggerKey.isEmpty())
223+
{
224+
return false;
225+
}
226+
227+
const QJsonObject ifProperties = ifObj.value("properties").toObject();
228+
const QJsonObject constObj = ifProperties.value(triggerKey).toObject();
229+
if (!constObj.contains("const"))
230+
{
231+
return false;
232+
}
233+
234+
_conditionalTriggerKey = triggerKey;
235+
_conditionalTriggerConst = constObj.value("const").toString();
236+
237+
const QJsonObject thenProps = thenObj.value("properties").toObject();
238+
for (auto it = thenProps.constBegin(); it != thenProps.constEnd(); ++it)
239+
{
240+
_thenKeys.append(it.key());
241+
}
242+
243+
const QJsonObject elseProps = elseObj.value("properties").toObject();
244+
for (auto it = elseProps.constBegin(); it != elseProps.constEnd(); ++it)
245+
{
246+
_elseKeys.append(it.key());
247+
}
248+
249+
return true;
250+
}
251+
252+
void SchemaFormWidget::applyConditional(const QString& triggerValue)
253+
{
254+
_currentTriggerValue = triggerValue;
255+
const bool conditionMet = (triggerValue == _conditionalTriggerConst);
256+
257+
for (const auto& [key, widget] : std::as_const(_fields))
258+
{
259+
const bool isThen = _thenKeys.contains(key);
260+
const bool isElse = _elseKeys.contains(key);
261+
262+
if (!isThen && !isElse)
263+
{
264+
continue;
265+
}
266+
267+
const bool visible = isThen ? conditionMet : !conditionMet;
268+
widget->setVisible(visible);
269+
270+
QWidget* label = _pFormLayout->labelForField(widget);
271+
if (label != nullptr)
272+
{
273+
label->setVisible(visible);
274+
}
275+
}
276+
}
277+
278+
void SchemaFormWidget::onTriggerChanged(int /*index*/)
279+
{
280+
auto* combo = qobject_cast<QComboBox*>(sender());
281+
if (combo == nullptr)
282+
{
283+
return;
284+
}
285+
286+
applyConditional(combo->currentData().toString());
287+
}
288+
158289
QJsonObject SchemaFormWidget::values() const
159290
{
291+
const bool conditionMet = !_conditionalTriggerKey.isEmpty() && (_currentTriggerValue == _conditionalTriggerConst);
292+
160293
QJsonObject result;
161294
for (const auto& [key, widget] : _fields)
162295
{
296+
const bool isThen = _thenKeys.contains(key);
297+
const bool isElse = _elseKeys.contains(key);
298+
299+
if (isThen && !conditionMet)
300+
{
301+
continue;
302+
}
303+
if (isElse && conditionMet)
304+
{
305+
continue;
306+
}
307+
163308
if (auto* spin = qobject_cast<QSpinBox*>(widget))
164309
{
165310
result[key] = spin->value();

src/customwidgets/schemaformwidget.h

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <QJsonObject>
55
#include <QJsonValue>
6+
#include <QStringList>
67
#include <QWidget>
78

89
class QFormLayout;
@@ -14,6 +15,11 @@ class QFormLayout;
1415
* number, boolean, and enum-constrained string/integer properties.
1516
* The label for each row is taken from the property's \c "title" field, falling
1617
* back to the property key name if no title is provided.
18+
*
19+
* Supports the JSON Schema Draft 7 \c if/then/else pattern with a single-property
20+
* \c const condition. When detected, the active branch's fields are shown and the
21+
* inactive branch's fields are hidden, and visibility updates live as the trigger
22+
* field changes.
1723
*/
1824
class SchemaFormWidget : public QWidget
1925
{
@@ -32,15 +38,51 @@ class SchemaFormWidget : public QWidget
3238

3339
/*!
3440
* \brief Return current form input as a JSON object.
35-
* \return A QJsonObject with one entry per schema property.
41+
*
42+
* Fields belonging to the inactive conditional branch are omitted.
43+
* \return A QJsonObject with one entry per visible schema property.
3644
*/
3745
QJsonObject values() const;
3846

47+
private slots:
48+
//! Called when the trigger combo selection changes; re-evaluates conditional visibility.
49+
void onTriggerChanged(int index);
50+
3951
private:
4052
QWidget* createWidgetForProperty(const QJsonObject& propSchema, const QJsonValue& value);
4153

54+
/*!
55+
* \brief Parse the top-level \c if/then/else block and populate conditional state.
56+
*
57+
* Only the narrow single-property \c const pattern is supported.
58+
* \param schema Full schema object passed to setSchema().
59+
* \return \c true if a supported pattern was found and state was populated.
60+
*/
61+
bool parseConditional(const QJsonObject& schema);
62+
63+
/*!
64+
* \brief Show or hide rows based on whether the trigger value matches the const.
65+
* \param triggerValue Current string value of the trigger field.
66+
*/
67+
void applyConditional(const QString& triggerValue);
68+
4269
QFormLayout* _pFormLayout;
4370
QList<QPair<QString, QWidget*>> _fields;
71+
72+
//! Key in \c "properties" whose value drives the if/then/else switch.
73+
QString _conditionalTriggerKey;
74+
75+
//! The \c "const" value that activates the \c "then" branch.
76+
QString _conditionalTriggerConst;
77+
78+
//! Keys belonging to the \c "then" branch (shown when condition is true).
79+
QStringList _thenKeys;
80+
81+
//! Keys belonging to the \c "else" branch (shown when condition is false).
82+
QStringList _elseKeys;
83+
84+
//! Current value of the trigger field; used to filter \c values() output.
85+
QString _currentTriggerValue;
4486
};
4587

4688
#endif // SCHEMAFORMWIDGET_H

0 commit comments

Comments
 (0)