| 150 | } |
| 151 | |
| 152 | nlohmann::json ParameterForm::values() const { |
| 153 | nlohmann::json out = nlohmann::json::object(); |
| 154 | for (const Row& r : rows_) { |
| 155 | const std::string& key = r.spec.name; |
| 156 | switch (r.spec.type) { |
| 157 | case ParamType::kInteger: |
| 158 | out[key] = qobject_cast<QSpinBox*>(r.editor)->value(); |
| 159 | break; |
| 160 | case ParamType::kNumber: { |
| 161 | bool ok = false; |
| 162 | const double d = qobject_cast<QLineEdit*>(r.editor)->text().toDouble(&ok); |
| 163 | // Empty/intermediate text must not silently become 0 (e.g. binary_tol |
| 164 | // 1e-9 → 0): fall back to the declared default. |
| 165 | out[key] = ok ? d : (r.spec.default_value.is_number() ? r.spec.default_value.get<double>() : 0.0); |
| 166 | break; |
| 167 | } |
| 168 | case ParamType::kBoolean: |
| 169 | out[key] = qobject_cast<ToggleSwitch*>(r.editor)->isChecked(); |
| 170 | break; |
| 171 | case ParamType::kEnum: { |
| 172 | auto* cb = qobject_cast<ComboBox*>(r.editor); |
| 173 | const int idx = cb->currentIndex(); |
| 174 | if (idx >= 0 && idx < static_cast<int>(r.spec.values.size())) { |
| 175 | out[key] = r.spec.values[idx].value; |
| 176 | } else if (!r.spec.values.empty()) { |
| 177 | out[key] = r.spec.values.front().value; // out-of-range selection → declared first option |
| 178 | } else if (!r.spec.default_value.is_null()) { |
| 179 | out[key] = r.spec.default_value; |
| 180 | } else { |
| 181 | out[key] = nullptr; // never omit the key: a create() relying on it must see *something* |
| 182 | } |
| 183 | break; |
| 184 | } |
| 185 | case ParamType::kString: |
| 186 | out[key] = qobject_cast<QLineEdit*>(r.editor)->text().toStdString(); |
| 187 | break; |
| 188 | case ParamType::kText: |
| 189 | out[key] = qobject_cast<QPlainTextEdit*>(r.editor)->toPlainText().toStdString(); |
| 190 | break; |
| 191 | } |
| 192 | } |
| 193 | return out; |
| 194 | } |
| 195 | |
| 196 | void ParameterForm::setValues(const nlohmann::json& values) { |
| 197 | silent_ = true; |