23 const QVariantMap map = value.toMap();
25 const QString aliasValue = map.value(QStringLiteral(
"value")).toString();
26 if (!aliasValue.isEmpty()) {
29 const QString aliasSource = map.value(QStringLiteral(
"source")).toString();
30 if (!aliasSource.isEmpty()) {
33 const QString
id = map.value(QStringLiteral(
"id")).toString();
38 return value.toString();
41QVariantList normalizeStringValues(
const QVariantList& values) {
43 out.reserve(values.size());
44 for (
const auto& value : values) {
50QString rowIdAt(
const QVariantList& rows,
int index,
52 if (index < 0 || index >= rows.size()) {
55 return rows.at(index).toMap().value(idKey).toString();
58int rowIndexOfId(
const QVariantList& rows,
const QString&
id,
63 for (
int i = 0; i < rows.size(); ++i) {
64 if (rows.at(i).toMap().value(idKey).toString() ==
id) {
71QVariantMap rowById(
const QVariantList& rows,
const QString&
id,
73 const int index = rowIndexOfId(rows,
id.trimmed(), idKey);
74 return index >= 0 ? rows.at(index).toMap() : QVariantMap{};
78navigatedSelectionId(
const QVariantList& rows,
const QString& currentId,
79 int delta,
int defaultIndex = 0,
81 const int currentIndex = rowIndexOfId(rows, currentId, idKey);
82 if (currentIndex < 0) {
87 return rowIdAt(rows, 0, idKey);
90 return rowIdAt(rows, rows.size() - 1, idKey);
92 return rowIdAt(rows, defaultIndex, idKey);
95 return currentIndex >= rows.size() - 1
97 : rowIdAt(rows, currentIndex + 1, idKey);
100 return currentIndex <= 0 ? QString()
101 : rowIdAt(rows, currentIndex - 1, idKey);
103 return rowIdAt(rows, currentIndex, idKey);
107deleteNextSelectionId(
const QVariantList& rows,
const QString& removedId,
108 int defaultIndex = 0,
110 const int removedIndex = rowIndexOfId(rows, removedId, idKey);
111 if (rows.isEmpty()) {
114 const int nextIndex = removedIndex >= 0 ? removedIndex + 1 : defaultIndex;
115 const int wrapped = nextIndex % rows.size();
116 return rowIdAt(rows, wrapped < 0 ? wrapped + rows.size() : wrapped, idKey);
119bool canAddText(
const QString& value) {
120 return !value.trimmed().isEmpty();
123bool canRemoveAt(
const QVariantList& values,
int index) {
124 return index >= 0 && index < values.size();
127bool containsTrimmed(
const QVariantList& values,
const QString&
id) {
128 const QString nextId =
id.trimmed();
129 if (nextId.isEmpty()) {
132 for (
const auto& value : values) {
140QVariantList addUniqueTrimmed(
const QVariantList& values,
141 const QString& value) {
142 const QString next = value.trimmed();
143 QVariantList out = normalizeStringValues(values);
144 if (next.isEmpty() || containsTrimmed(out, next)) {
151QVariantList removeTrimmed(
const QVariantList& values,
const QString& value) {
152 const QString target = value.trimmed();
153 if (target.isEmpty()) {
157 out.reserve(values.size());
158 for (
const auto& entry : values) {
159 const QString current =
stringValue(entry).trimmed();
160 if (current != target) {
161 out.push_back(current);
167QVariantList removeAt(
const QVariantList& values,
int index) {
168 QVariantList out = normalizeStringValues(values);
169 if (!canRemoveAt(out, index)) {
176int aliasIndexAfterRemoval(
const QVariantList& aliases,
int removedIndex) {
177 if (aliases.isEmpty()) {
180 return removedIndex < aliases.size() ? removedIndex : aliases.size() - 1;
183bool appendAliasValue(QVariantList& aliases, QString& aliasInputText,
184 int& aliasIndex,
const QString& value) {
185 const QVariantList next = addUniqueTrimmed(aliases, value);
186 if (next == aliases) {
190 aliasIndex = aliases.isEmpty() ? -1 : aliases.size() - 1;
191 aliasInputText.clear();
196 const QVariantList next = removeAt(aliases, index);
197 if (next == aliases) {
201 aliasIndex = aliasIndexAfterRemoval(aliases, index);
205bool setSelectedId(QVariantList& selectedIds,
const QString&
id,
207 const QString normalizedId =
id.trimmed();
208 if (normalizedId.isEmpty()) {
211 const QVariantList next = selected
212 ? addUniqueTrimmed(selectedIds, normalizedId)
213 : removeTrimmed(selectedIds, normalizedId);
214 if (next == selectedIds) {
221QStringList normalizedStringList(
const QVariantList& values) {
222 const QVariantList normalized = normalizeStringValues(values);
224 out.reserve(normalized.size());
225 for (
const auto& value : normalized) {
226 out.push_back(value.toString());
232bool formStateChanged(
const QString& savedName,
233 const QVariantList& savedAliases,
234 const QVariantList& savedContractIds,
235 const QString& currentName,
const QVariantList& aliases,
236 const QVariantList& contractIds) {
237 return savedName != currentName ||
238 normalizedStringList(savedAliases) != normalizedStringList(aliases) ||
239 normalizedStringList(savedContractIds) !=
240 normalizedStringList(contractIds);
243QVariantMap propertyFormStateFromRow(
const QString& name,
244 const QVariantList& aliases,
245 const QVariantList& selectedIds = {}) {
247 const QVariantList normalizedAliases = normalizeStringValues(aliases);
252 normalizedAliases.isEmpty() ? -1 : 0);
254 normalizeStringValues(selectedIds));
265 : QObject(parent), store_(store), commands_(commands),
266 selection_(selection), selectors_(selectors) {
268 reloadFromSelection(
true);
272 if (name_ == value) {
280 if (aliases_ == value) {
284 if (aliasIndex_ >= aliases_.size()) {
285 aliasIndex_ = aliases_.isEmpty() ? -1 : aliases_.size() - 1;
291 if (aliasInputText_ == value) {
294 aliasInputText_ = value;
299 if (aliasIndex_ == value) {
307 if (selectedContractIds_ == value) {
310 selectedContractIds_ = value;
319 return !name_.trimmed().isEmpty();
326 return formStateChanged(savedName_, savedAliases_, savedSelectedContractIds_,
327 name_, aliases_, selectedContractIds_);
331 return canAddText(value);
334bool PropertyViewModel::hasValidAliasSelection()
const {
335 return canRemoveAt(aliases_, aliasIndex_);
339 if (!appendAliasValue(aliases_, aliasInputText_, aliasIndex_, value)) {
346 if (!hasValidAliasSelection() ||
347 !removeAliasValue(aliases_, aliasIndex_, aliasIndex_)) {
355 currentOwnerId_.clear();
362 if (rows.isEmpty()) {
365 setCurrentId(navigatedSelectionId(rows,
isEdit() ?
currentId() : QString(),
366 -1, rows.size() - 1));
371 if (rows.isEmpty()) {
383 "PropertyViewModel::submit",
384 isEdit() ?
"Property update submitted" :
"Property create submitted",
387 const QString
id = commands_->saveProperty(
399 if (!commands_ ||
currentId().isEmpty()) {
403 "PropertyViewModel::deleteCurrent",
"Property delete submitted",
406 commands_->deleteProperty(removedId);
407 setCurrentId(deleteNextSelectionId(
propertyRows(), removedId, 0,
408 QStringLiteral(
"id")));
412 currentOwnerId_.clear();
419 if (!setSelectedId(selectedContractIds_, contractId, selected)) {
426 return selection_ ? selection_->selectedPropertyId() : QString();
430 return selectors_ ? selectors_->propertyRows() : QVariantList{};
434 return selectors_ ? selectors_->contractRows() : QVariantList{};
437void PropertyViewModel::setCurrentId(
const QString&
id) {
447void PropertyViewModel::bindSignals() {
448 if (!store_ || !selection_) {
454 reloadFromSelection(
true);
458 reloadFromSelection(
false);
462void PropertyViewModel::reloadFromSelection(
bool forceReload) {
463 if (!selection_ || !selectors_) {
468 if (selectedId.isEmpty()) {
469 reloadFormState(forceReload, {});
473 const QVariantMap row = rowById(
propertyRows(), selectedId);
474 const QVariantMap state =
477 : propertyFormStateFromRow(
481 reloadFormState(forceReload, state);
484void PropertyViewModel::clearFormState() {
487 aliasInputText_.clear();
489 selectedContractIds_.clear();
493void PropertyViewModel::captureSavedState() {
495 savedAliases_ = aliases_;
496 savedSelectedContractIds_ = selectedContractIds_;
500void PropertyViewModel::applyFormState(
const QVariantMap& state) {
501 name_ = state.value(QStringLiteral(
"name")).toString();
502 aliases_ = state.value(QStringLiteral(
"aliases")).toList();
503 aliasInputText_ = state.value(QStringLiteral(
"aliasInputText")).toString();
505 state.value(QStringLiteral(
"aliasIndex"), aliases_.isEmpty() ? -1 : 0)
507 selectedContractIds_ = state.value(QStringLiteral(
"selectedIds")).toList();
511void PropertyViewModel::reloadFormState(
bool forceReload,
512 const QVariantMap& state) {
514 if (!forceReload && currentOwnerId_ == nextOwnerId) {
518 currentOwnerId_ = nextOwnerId;
519 if (nextOwnerId.isEmpty() || state.isEmpty()) {
525 applyFormState(state);
Declarations for the UI PayloadKeys component.
Declarations for the UI payload mapper helpers.
Declares the QML API for the Property view.
Declarations for the UI Trace component.
Declares the workspace selection state synchronized with the store.
Declares read-only workspace selectors for QML payloads.
Declares the UI workspace snapshot store.
Q_INVOKABLE void addAlias(const QString &value)
Adds an alias to the property form.
Q_INVOKABLE void previous()
Selects the previous property row.
Q_INVOKABLE void selectProperty(const QString &id)
Selects a property for editing.
Q_INVOKABLE bool canAddAlias(const QString &value) const
Checks whether an alias value can be added to the form.
Q_INVOKABLE void clear()
Clears the editable property form.
Q_INVOKABLE void next()
Selects the next property row.
void changed()
Emitted when property view model state changed.
Q_INVOKABLE QString submit()
Creates or updates the property through workspace commands.
void setAliasInputText(const QString &value)
Updates the current alias input text.
QVariantList contractRows
Q_INVOKABLE void setContractSelected(const QString &contractId, bool selected)
Adds or removes a contract id from the property form selection.
QVariantList propertyRows
PropertyViewModel(WorkspaceStore *store, WorkspaceCommands *commands, WorkspaceSelection *selection, WorkspaceSelectors *selectors, QObject *parent=nullptr)
Creates the property view model bound to workspace roles.
Q_INVOKABLE void requestRemoveSelectedAlias()
Removes the currently selected alias when possible.
void setName(const QString &value)
Updates the editable property name.
void setAliases(const QVariantList &value)
Replaces editable property aliases.
Q_INVOKABLE void enterCreateMode()
Clears selection and opens an empty create form.
Q_INVOKABLE void deleteCurrent()
Deletes the selected property through workspace commands.
void setAliasIndex(int value)
Updates the selected alias index.
void setSelectedContractIds(const QVariantList &value)
Replaces selected contract ids in the property form.
Maps UI intent to core workspace writer commands.
Tracks the currently selected workspace entities.
void selectedPropertyIdChanged()
Emitted when the selected property id changed.
void selectProperty(const QString &id)
Selects a property.
Projects workspace store data into QML-facing read models.
Owns the current UI-side workspace snapshot and revision lifecycle.
void dataRevisionChanged()
void removeAliasValue(std::vector< Alias > &aliases, const std::string &value)
Removes aliases with a given value.
void traceViewModel(const char *origin, std::string message, core::errors::ErrorContext context={})
Emits a view model trace event.
const auto kAliasInputText
QString stringValue(const QVariantMap &map, const QString &key)
Reads a string value from a map.
QStringList toQStringList(const std::vector< std::string > &values)
Converts standard strings to a Qt string list.
Declares UI-side workspace command submission.