22#include " view-utils.hpp"
33#include < QJsonArray>
44#include < QJsonObject>
5+ #include < QJSValue>
6+ #include < qcontainerfwd.h>
7+ #include < qstringliteral.h>
58#include < utility>
69
710ExtensionFormModel::ExtensionFormModel (NotifyFn notify, QObject *parent)
@@ -54,7 +57,14 @@ void ExtensionFormModel::setFieldValue(int index, const QVariant &value) {
5457 auto &item = m_items[index];
5558 if (!item.isField ()) return ;
5659
57- item.userValue = QJsonValue::fromVariant (value);
60+ // TODO: find out why Form.TagPicker tags would break without that
61+ QVariant normalizedValue = value;
62+
63+ if (value.metaType ().id () == qMetaTypeId<QJSValue>()) {
64+ normalizedValue = value.value <QJSValue>().toVariant ();
65+ }
66+
67+ item.userValue = QJsonValue::fromVariant (normalizedValue);
5868 item.hasUserValue = true ;
5969 emit dataChanged (createIndex (index, 0 ), createIndex (index, 0 ), {ValueRole});
6070
@@ -99,13 +109,38 @@ void ExtensionFormModel::setFilePaths(int index, const QVariantList &paths) {
99109 if (item.onChange ) { m_notify (*item.onChange , QJsonArray{arr}); }
100110}
101111
112+ void ExtensionFormModel::setPickedItems (int index, const QVariantList &pickedItems) {
113+ if (index < 0 || std::cmp_greater_equal (index, m_items.size ())) return ;
114+ auto &item = m_items[index];
115+ if (item.type != FormItemData::Type::TagPicker) return ;
116+
117+ QJsonArray arr;
118+ for (const auto &t : pickedItems) {
119+ arr.append (t.toString ());
120+ }
121+
122+ item.userValue = arr;
123+ item.hasUserValue = true ;
124+ syncTagPickerSuggestionsModel (item);
125+ emit dataChanged (createIndex (index, 0 ), createIndex (index, 0 ), {ValueRole});
126+
127+ if (item.onChange ) { m_notify (*item.onChange , QJsonArray{arr}); }
128+ }
129+
102130void ExtensionFormModel::dropdownSearchTextChanged (int index, const QString &text) {
103131 if (index < 0 || std::cmp_greater_equal (index, m_items.size ())) return ;
104132 const auto &item = m_items[index];
105133 auto handler = item.fieldData .value (" onSearchTextChange" ).toString ();
106134 if (!handler.isEmpty ()) { m_notify (handler, QJsonArray{text}); }
107135}
108136
137+ void ExtensionFormModel::tagPickerSearchTextChanged (int index, const QString &text) {
138+ if (index < 0 || std::cmp_greater_equal (index, m_items.size ())) return ;
139+ auto &item = m_items[index];
140+ if (item.type != FormItemData::Type::TagPicker || !item.tagPickerSuggestionsModel ) return ;
141+ item.tagPickerSuggestionsModel ->setFilter (text);
142+ }
143+
109144std::optional<FileChooserOptions> ExtensionFormModel::filePickerOptions (int index) const {
110145 if (index < 0 || std::cmp_greater_equal (index, m_items.size ())) return std::nullopt ;
111146 const auto &item = m_items[index];
@@ -146,6 +181,7 @@ void ExtensionFormModel::setFormData(const FormModel &model) {
146181 if (it != savedValues.end ()) {
147182 newData.userValue = it->second ;
148183 newData.hasUserValue = true ;
184+ syncTagPickerSuggestionsModel (newData);
149185 }
150186 }
151187 m_items[i] = std::move (newData);
@@ -162,6 +198,7 @@ void ExtensionFormModel::setFormData(const FormModel &model) {
162198 if (it != savedValues.end ()) {
163199 newData.userValue = it->second ;
164200 newData.hasUserValue = true ;
201+ syncTagPickerSuggestionsModel (newData);
165202 }
166203 }
167204 m_items.push_back (std::move (newData));
@@ -207,13 +244,14 @@ ExtensionFormModel::FormItemData ExtensionFormModel::createItem(const FormModel:
207244 data.onChange = field->onChange ;
208245 data.onBlur = field->onBlur ;
209246 data.onFocus = field->onFocus ;
210- data.fieldData = buildFieldData (*field);
211247
212248 if (field->value ) {
213249 data.modelValue = *field->value ;
214250 } else if (field->defaultValue ) {
215251 data.modelValue = *field->defaultValue ;
216252 }
253+
254+ updateFieldData (data, *field);
217255 } else if (auto *desc = std::get_if<FormModel::Description>(&item)) {
218256 data.type = FormItemData::Type::Description;
219257 data.label = desc->title .value_or (QString ());
@@ -235,12 +273,13 @@ void ExtensionFormModel::updateItem(FormItemData &existing, const FormModel::Ite
235273 existing.onChange = field->onChange ;
236274 existing.onBlur = field->onBlur ;
237275 existing.onFocus = field->onFocus ;
238- existing.fieldData = buildFieldData (*field);
239276
240277 if (field->value ) {
241278 existing.modelValue = *field->value ;
242279 existing.hasUserValue = false ;
243280 }
281+
282+ updateFieldData (existing, *field);
244283 } else if (auto *desc = std::get_if<FormModel::Description>(&newItem)) {
245284 existing.label = desc->title .value_or (QString ());
246285 existing.fieldData = {{" text" , desc->text }};
@@ -255,10 +294,11 @@ ExtensionFormModel::FormItemData::Type ExtensionFormModel::fieldType(const FormM
255294 if (dynamic_cast <const FormModel::TextAreaField *>(&field)) return FormItemData::Type::TextArea;
256295 if (dynamic_cast <const FormModel::FilePickerField *>(&field)) return FormItemData::Type::FilePicker;
257296 if (dynamic_cast <const FormModel::DatePickerField *>(&field)) return FormItemData::Type::DatePicker;
297+ if (dynamic_cast <const FormModel::TagPickerField *>(&field)) return FormItemData::Type::TagPicker;
258298 return FormItemData::Type::Text;
259299}
260300
261- QVariantMap ExtensionFormModel::buildFieldData ( const FormModel::IField &field) {
301+ void ExtensionFormModel::updateFieldData (FormItemData &item, const FormModel::IField &field) const {
262302 QVariantMap data;
263303
264304 if (auto *f = dynamic_cast <const FormModel::CheckboxField *>(&field)) {
@@ -280,9 +320,35 @@ QVariantMap ExtensionFormModel::buildFieldData(const FormModel::IField &field) {
280320 if (f->min ) data[" min" ] = *f->min ;
281321 if (f->max ) data[" max" ] = *f->max ;
282322 data[" includeTime" ] = f->type .value_or (" date" ) == " dateTime" ;
323+ } else if (auto *f = dynamic_cast <const FormModel::TagPickerField *>(&field)) {
324+ data[" items" ] = qml::convertTagPickerItems (f->m_items );
325+ if (!item.tagPickerSuggestionsModel ) {
326+ item.tagPickerSuggestionsModel = std::make_unique<FormTagPickerSuggestionsModel>();
327+ }
328+ item.tagPickerSuggestionsModel ->setSourceItems (f->m_items );
329+ syncTagPickerSuggestionsModel (item);
330+ data[" suggestionsModel" ] =
331+ QVariant::fromValue (static_cast <QObject *>(item.tagPickerSuggestionsModel .get ()));
283332 }
284333
285- return data;
334+ item.fieldData = std::move (data);
335+ }
336+
337+ void ExtensionFormModel::syncTagPickerSuggestionsModel (FormItemData &item) const {
338+ if (!item.tagPickerSuggestionsModel ) return ;
339+ item.tagPickerSuggestionsModel ->setPickedItems (tagPickerPickedItems (item.effectiveValue ()));
340+ }
341+
342+ QStringList ExtensionFormModel::tagPickerPickedItems (const QJsonValue &value) {
343+ QStringList result;
344+ if (!value.isArray ()) return result;
345+
346+ auto const arr = value.toArray ();
347+ result.reserve (arr.size ());
348+ for (const auto &entry : arr) {
349+ if (entry.isString ()) { result.append (entry.toString ()); }
350+ }
351+ return result;
286352}
287353
288354QString ExtensionFormModel::FormItemData::typeString () const {
@@ -305,6 +371,8 @@ QString ExtensionFormModel::FormItemData::typeString() const {
305371 return QStringLiteral (" description" );
306372 case Type::Separator:
307373 return QStringLiteral (" separator" );
374+ case Type::TagPicker:
375+ return QStringLiteral (" tagpicker" );
308376 }
309377 return QStringLiteral (" text" );
310378}
0 commit comments