Fix: Show indexed coFloatsOrPercents options in unsaved changes dialog (#15472)

This commit is contained in:
Valerii Bokhan
2026-09-17 10:56:17 -03:00
committed by GitHub
parent 7065fa9eae
commit 60b4a61854
4 changed files with 100 additions and 32 deletions
+11
View File
@@ -188,6 +188,17 @@ wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig&
out = double_to_string(opt->value) + (opt->percent ? "%" : "");
return out;
}
case coFloatsOrPercents: {
const auto* values = static_cast<const ConfigOptionVector<FloatOrPercent>*>(option);
// Orca: Preset comparison may request the entire vector instead of an indexed entry.
if (orig_opt_idx < 0)
return from_u8(option->serialize());
if (opt_idx < values->size()) {
const FloatOrPercent& value = values->get_at(opt_idx);
return double_to_string(value.value) + (value.percent ? "%" : "");
}
return _L("Undefined");
}
case coEnum: {
return get_string_from_enum(pure_key, config,
pure_key == "top_surface_pattern" ||
+34 -16
View File
@@ -62,7 +62,7 @@ static char marker_by_type(Preset::Type type, PrinterTechnology pt)
}
}
std::string Option::opt_key() const { return into_u8(key).substr(2); }
std::string Option::opt_key() const { return key.size() < 2 ? std::string() : into_u8(key).substr(2); }
void FoundOption::get_marked_label_and_tooltip(const char **label_, const char **tooltip_) const
{
@@ -116,6 +116,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty
case coFloats: change_opt_key<ConfigOptionFloats>(opt_key, config, cnt); break;
case coStrings: change_opt_key<ConfigOptionStrings>(opt_key, config, cnt); break;
case coPercents: change_opt_key<ConfigOptionPercents>(opt_key, config, cnt); break;
case coFloatsOrPercents: change_opt_key<ConfigOptionVector<FloatOrPercent>>(opt_key, config, cnt); break;
case coPoints: change_opt_key<ConfigOptionPoints>(opt_key, config, cnt); break;
// BBS
case coEnums: change_opt_key<ConfigOptionInts>(opt_key, config, cnt); break;
@@ -334,29 +335,46 @@ const Option &OptionsSearcher::get_option(size_t pos_in_filter) const
const Option &OptionsSearcher::get_option(const std::string &opt_key, Preset::Type type, int &variant_index) const
{
auto not_found = [&variant_index]() -> const Option& {
static const Option empty_option;
variant_index = -2;
return empty_option;
};
variant_index = -1;
std::string opt_key2 = opt_key;
if (auto n = opt_key.find('#'); n != std::string::npos) {
variant_index = std::atoi(opt_key.c_str() + n + 1);
opt_key2 = opt_key.substr(0, n);
}
auto it = std::lower_bound(options.begin(), options.end(), Option({boost::nowide::widen(get_key(opt_key2, type))}));
// BBS: return the 0th option when not found in searcher caused by mode difference
// assert(it != options.end());
if (it == options.end()) { variant_index = -2 ; return options[0]; }
if (it->opt_key() == opt_key2) {
const std::wstring key = boost::nowide::widen(get_key(opt_key2, type));
auto it = std::lower_bound(options.begin(), options.end(), Option({key}));
if (it == options.end()) return not_found();
if (it->key == key) {
variant_index = -1;
} else {
const std::string opt_key3 = opt_key2 + "#";
it = std::lower_bound(it, options.end(), Option({boost::nowide::widen(get_key(opt_key3, type))}));
if (it == options.end() || it->opt_key().compare(0, opt_key3.length(), opt_key3) != 0) {
variant_index = -2; // Not found
return options[0];
const std::wstring prefix = key + L"#";
it = std::lower_bound(it, options.end(), Option({prefix}));
if (it == options.end() || it->key.compare(0, prefix.length(), prefix) != 0)
return not_found();
// Orca: Copy-parameters dialogs request the base key, without a vector index.
if (variant_index < 0) return *it;
const bool has_mode = type == Preset::TYPE_PRINTER && printer_options_with_variant_2.count(opt_key2) > 0;
const bool has_variant =
(type == Preset::TYPE_PRINT && print_options_with_variant.count(opt_key2) > 0) ||
(type == Preset::TYPE_FILAMENT && filament_options_with_variant.count(opt_key2) > 0) ||
(type == Preset::TYPE_PRINTER && printer_options_with_variant_1.count(opt_key2) > 0) || has_mode;
if (!has_variant || has_mode) {
// Orca: Machine limits store (Normal, Silent) pairs per variant; the UI registers only #0/#1.
const std::wstring indexed_key = has_mode ? prefix + std::to_wstring(variant_index % 2) :
boost::nowide::widen(get_key(opt_key, type));
it = std::lower_bound(it, options.end(), Option({indexed_key}));
if (it == options.end() || it->key != indexed_key)
return not_found();
if (!has_variant)
variant_index = -1;
}
auto it2 = it;
++it2;
if (it2 != options.end() && it2->opt_key().compare(0, opt_key3.length(), opt_key3) == 0
&& printer_options_with_variant_1.find(opt_key2) == printer_options_with_variant_1.end())
variant_index = -2;
}
return options[it - options.begin()];
+38 -16
View File
@@ -1490,7 +1490,15 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, DynamicConfig * config
for (const std::string &opt_key : config->keys()) {
int variant_index = -2;
const Search::Option &option = searcher.get_option(opt_key, type, variant_index);
Search::Option option = searcher.get_option(opt_key, type, variant_index);
if (variant_index == -2) {
// Orca: Every transferred setting must remain visible even when it is absent from the search index.
const ConfigOptionDef* def = print_config_def.get(opt_key);
const std::string label = def ? (def->full_label.empty() ? def->label : def->full_label) : std::string();
option.label_local = (label.empty() ? from_u8(opt_key) : _L(label)).ToStdWstring();
option.category_local = (def && !def->category.empty() ?
Tab::translate_category(from_u8(def->category), type) : _L("Other")).ToStdWstring();
}
auto category = option.category_local;
auto opt = dynamic_cast<ConfigOptionVectorBase*>(config->option(opt_key));
std::string value_from = opt->vserialize()[from];
@@ -1518,6 +1526,8 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
else
presets_list.emplace_back(presets_);
const bool multiple_extruders = wxGetApp().preset_bundle->get_printer_extruder_count() > 1;
// Display a dialog showing the dirty options in a human readable form.
for (PresetCollection* presets : presets_list)
{
@@ -1553,29 +1563,41 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
auto variant_key = Preset::get_iot_type_string(type) + "_extruder_variant";
auto id_key = Preset::get_iot_type_string(type) + "_extruder_id";
auto extruder_variant = dynamic_cast<ConfigOptionStrings const *>(old_config.option(variant_key));
auto extruder_id = dynamic_cast<ConfigOptionInts const *>(old_config.option(id_key));
// Orca: Dirty indices belong to the edited config, which may contain newly added variants.
auto extruder_variant = dynamic_cast<ConfigOptionStrings const *>(new_config.option(variant_key));
auto extruder_id = dynamic_cast<ConfigOptionInts const *>(new_config.option(id_key));
for (const std::string& opt_key : dirty_options) {
int variant_index = -2;
const Search::Option &option = searcher.get_option(opt_key, type, variant_index);
if (option.opt_key() != opt_key && variant_index < -1) {
if (variant_index == -2) {
// When founded option isn't the correct one.
// It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id",
// because of they don't exist in searcher
continue;
}
auto category = option.category_local;
if (variant_index >= 0) {
if (printer_options_with_variant_2.count(opt_key.substr(0, opt_key.find_last_of('#'))) > 0)
variant_index /= 2;
if (boost::nowide::narrow(category).find("Extruder ") == 0)
category = category.substr(0, 8);
if (extruder_id)
category = category + (wxString(" {") + (extruder_id->values[variant_index] == 1 ? _L("Left: ") : _L("Right: "))
+ L(extruder_variant->values[variant_index]) + "}");
else
category = category + (wxString(" {") + L(extruder_variant->values[variant_index]) + "}");
wxString category = option.category_local;
wxString label = option.label_local;
if (type == Preset::TYPE_PRINTER && variant_index >= 0 &&
printer_options_with_variant_2.count(get_pure_opt_key(opt_key)) > 0) {
// Orca: silent_mode is obsolete on import, but its option and two-column UI still exist.
// Keep mode labels for configs that explicitly enable it; omit them in the default single-mode UI.
if (new_config.opt_bool("silent_mode"))
label += " (" + (variant_index % 2 == 0 ? _L("Normal") : _L("Silent")) + ")";
variant_index /= 2;
}
if (variant_index >= 0 && extruder_variant && variant_index < extruder_variant->size()) {
// Orca: Match the untranslated category and use the same extruder names as the printer tabs.
if (option.category.compare(0, 9, L"Extruder ") == 0)
category = _L("Extruder");
wxString variant_label = L(extruder_variant->values[variant_index]);
// Orca: An extruder name only disambiguates variants on printers with multiple extruders.
if (multiple_extruders && extruder_id && variant_index < extruder_id->size() && extruder_id->values[variant_index] > 0) {
const wxString extruder_name = Tab::translate_category(
wxString::Format("Extruder %d", extruder_id->values[variant_index]), Preset::TYPE_PRINTER);
variant_label = extruder_name + " (" + variant_label + ")";
}
category = variant_label + ": " + category;
}
/*m_tree->Append(opt_key, type, option.category_local, option.group_local, option.label_local,
@@ -1584,7 +1606,7 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
//PresetItem pi = {opt_key, type, 1983};
//m_presetitems.push_back()
PresetItem pi = {type, opt_key, category, option.group_local, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)};
PresetItem pi = {type, opt_key, category, option.group_local, label, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)};
m_presetitems.push_back(pi);
}
+17
View File
@@ -33,3 +33,20 @@ TEST_CASE("deep_diff flags new vector entries that duplicate values[0]", "[Prese
// specific to new indices rather than flagging the whole vector.
REQUIRE(std::find(diff.begin(), diff.end(), "nozzle_diameter#0") == diff.end());
}
TEST_CASE("deep_diff distinguishes absolute and percentage speeds for each variant", "[PresetDiff][Config]")
{
const size_t changed_index = GENERATE(size_t(0), size_t(1));
Preset reference(Preset::TYPE_PRINT, "ref");
reference.config.set_key_value("small_perimeter_speed", new ConfigOptionFloatsOrPercents{{50., false}, {50., false}});
Preset edited = reference;
edited.config.option<ConfigOptionFloatsOrPercents>("small_perimeter_speed")->values[changed_index].percent = true;
const auto diff = PresetCollection::dirty_options(&edited, &reference, /*deep_compare=*/true);
REQUIRE(diff == std::vector<std::string>{"small_perimeter_speed#" + std::to_string(changed_index)});
DynamicPrintConfig transferred = reference.config;
transferred.apply_only(edited.config, diff);
REQUIRE(*transferred.option("small_perimeter_speed") == *edited.config.option("small_perimeter_speed"));
}