From 24cc5e7aec9bdd6d6fc19aa39907d59ba01f3a42 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Tue, 17 Feb 2026 07:57:53 -0500 Subject: [PATCH 01/14] Add ToggleExpr and convert Tab logic --- src/libslic3r/Utils.hpp | 17 ++++ src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Field.hpp | 2 + src/slic3r/GUI/OptionsGroup.hpp | 1 + src/slic3r/GUI/Tab.cpp | 169 ++++++++++++++++++++------------ src/slic3r/GUI/Tab.hpp | 11 ++- src/slic3r/GUI/ToggleExpr.cpp | 101 +++++++++++++++++++ src/slic3r/GUI/ToggleExpr.hpp | 138 ++++++++++++++++++++++++++ 8 files changed, 380 insertions(+), 61 deletions(-) create mode 100644 src/slic3r/GUI/ToggleExpr.cpp create mode 100644 src/slic3r/GUI/ToggleExpr.hpp diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 5c71553132..89b6a59499 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -74,6 +74,23 @@ #define CLI_GCODE_PATH_IN_UNPRINTABLE_AREA -102 +#ifdef _WIN32 +#define PUSH_IGNORE_DEPRECATED \ +_Pragma("warning(push)") \ +_Pragma("warning(disable: 4996)") +#else +#define PUSH_IGNORE_DEPRECATED \ +_Pragma("GCC diagnostic push")\ +_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#endif + +#ifdef _WIN32 +#define POP_IGNORE_DEPRECATED \ +_Pragma("warning(pop)") +#else +#define PUSH_IGNORE_DEPRECATED \ +_Pragma("GCC diagnostic pop") +#endif namespace boost { namespace filesystem { class directory_entry; }} namespace Slic3r { diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7ec3215940..853b6ea050 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -649,6 +649,8 @@ set(SLIC3R_GUI_SOURCES Utils/WxFontUtils.hpp Utils/FileTransferUtils.cpp Utils/FileTransferUtils.hpp + GUI/ToggleExpr.cpp + GUI/ToggleExpr.hpp ) add_subdirectory(GUI/DeviceCore) diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index b2e615842a..a658ffe128 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -206,6 +206,8 @@ public: /// Callback function to edit field value t_back_to_init m_fn_edit_value{ nullptr }; + std::vector disabled_reasons{}; + // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; bool m_is_modified_value {false}; diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index ce523cbedc..f1d86c3e81 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -57,6 +57,7 @@ public: std::string label_path; bool undo_to_sys{false}; // BBS: object config bool toggle_visible{true}; // BBS: hide some line + std::vector hidden_reasons; // Why was the visibility toggled size_t full_width {0}; widget_t widget {nullptr}; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 75b6dbb54e..a92f4a9dd5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -57,6 +57,8 @@ #include #endif // WIN32 +#include "ToggleExpr.hpp" + #include namespace Slic3r { @@ -1420,6 +1422,20 @@ void Tab::toggle_option(const std::string& opt_key, bool toggle, int opt_index/* field->toggle(toggle); } +void Tab::toggle_option(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index /*= -1*/) +{ + if (!m_active_page) + return; + Field* field = m_active_page->get_field(opt_key, opt_index); + if (field) { + field->toggle(toggle_expr.get_value()); + if (toggle_expr.get_value()) + field->disabled_reasons.clear(); + else + field->disabled_reasons = toggle_expr.get_reasons(); + } +} + void Tab::toggle_line(const std::string &opt_key, bool toggle, int opt_index) { if (!m_active_page) return; @@ -1427,6 +1443,19 @@ void Tab::toggle_line(const std::string &opt_key, bool toggle, int opt_index) if (line) line->toggle_visible = toggle; }; +void Tab::toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index /*= -1*/) +{ + if (!m_active_page) return; + Line *line = m_active_page->get_line(opt_key, opt_index); + if (line) { + line->toggle_visible = toggle_expr.get_value(); + if (toggle_expr.get_value()) + line->hidden_reasons.clear(); + else + line->hidden_reasons = toggle_expr.get_reasons(); + } +} + // To be called by custom widgets, load a value into a config, // update the preset selection boxes (the dirty flags) // If value is saved before calling this function, put saved_value = true, @@ -3735,16 +3764,20 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print if (opt_key == "filament_long_retractions_when_cut") { int machine_enabled_level = printers_config->option( "enable_long_retraction_when_cut")->value; - bool machine_enabled = machine_enabled_level == LongRectrationLevel::EnableFilament; - toggle_line(opt_key, machine_enabled, extruder_idx + 256); - field->toggle(is_checked && machine_enabled); + auto machine_enabled = ToggleExpr(machine_enabled_level == LongRectrationLevel::EnableFilament, + "enable_long_retraction_when_cut is not enabled for filament") + .disable_postfix(); + toggle_line(opt_key, machine_enabled, extruder_idx + 256); + field->toggle(is_checked && machine_enabled.get_value()); } else if (opt_key == "filament_retraction_distances_when_cut") { int machine_enabled_level = printers_config->option( "enable_long_retraction_when_cut")->value; - bool machine_enabled = machine_enabled_level == LongRectrationLevel::EnableFilament; - bool filament_enabled = m_config->option("filament_long_retractions_when_cut")->values[extruder_idx] == 1; + auto machine_enabled = ToggleExpr(machine_enabled_level == LongRectrationLevel::EnableFilament, + "enable_long_retraction_when_cut is not enabled for filament") + .disable_postfix(); + auto filament_enabled = ToggleExpr::FromConfigBool(m_config, "filament_long_retractions_when_cut", extruder_idx); toggle_line(opt_key, filament_enabled && machine_enabled, extruder_idx + 256); - field->toggle(is_checked && filament_enabled && machine_enabled); + field->toggle(is_checked && (filament_enabled && machine_enabled).get_value()); } else { if (!is_checked) { const std::string printer_opt_key = opt_key.substr(strlen("filament_")); @@ -4152,34 +4185,36 @@ void TabFilament::toggle_options() { if (!m_active_page) return; - bool is_BBL_printer = false; + bool b_is_BBL_printer = false; if (m_preset_bundle) { - is_BBL_printer = + b_is_BBL_printer = wxGetApp().preset_bundle->is_bbl_vendor(); } - bool is_multi_extruder = m_preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->size() > 1; + auto is_BBL_printer = ToggleExpr(b_is_BBL_printer, "BBL printer").set_prefixes("Isn't ", "Is ").disable_postfix(); + auto is_multi_extruder = ToggleExpr(m_preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->size() > 1, "Not multi extruder").disable_postfix(); auto cfg = m_preset_bundle->printers.get_edited_preset().config; if (m_active_page->title() == L("Cooling")) { - bool has_enable_overhang_bridge_fan = m_config->opt_bool("enable_overhang_bridge_fan", 0); + auto has_enable_overhang_bridge_fan = ToggleExpr::FromConfigBool(m_config, "enable_overhang_bridge_fan", 0); for (auto el : {"overhang_fan_speed", "overhang_fan_threshold", "internal_bridge_fan_speed"}) // ORCA: Add support for separate internal bridge fan speed control toggle_option(el, has_enable_overhang_bridge_fan); - toggle_option("additional_cooling_fan_speed", cfg.opt_bool("auxiliary_fan")); + toggle_option("additional_cooling_fan_speed", ToggleExpr::FromConfigBool(&cfg, "auxiliary_fan")); // Orca: toggle dont slow down for external perimeters if - bool has_slow_down_for_layer_cooling = m_config->opt_bool("slow_down_for_layer_cooling", 0); + auto has_slow_down_for_layer_cooling = ToggleExpr::FromConfigBool(m_config, "slow_down_for_layer_cooling", 0); toggle_option("dont_slow_down_outer_wall", has_slow_down_for_layer_cooling); } if (m_active_page->title() == L("Filament")) { - bool pa = m_config->opt_bool("enable_pressure_advance", 0); + auto pa = ToggleExpr::FromConfigBool(m_config, "enable_pressure_advance", 0); toggle_option("pressure_advance", pa); //Orca: Enable the plates that should be visible when multi bed support is enabled or a BBL printer is selected; otherwise, enable only the plate visible for the selected bed type. DynamicConfig& proj_cfg = m_preset_bundle->project_config; - std::string bed_temp_1st_layer_key = ""; - if (proj_cfg.has("curr_bed_type")) + std::string bed_temp_1st_layer_key; + auto has_bed_type= ToggleExpr(proj_cfg.has("curr_bed_type"), "curr_bed_type").set_postfixes(" not set", " is set"); + if (has_bed_type.get_value()) { bed_temp_1st_layer_key = get_bed_temp_1st_layer_key(proj_cfg.opt_enum("curr_bed_type")); } @@ -4188,13 +4223,16 @@ void TabFilament::toggle_options() "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "textured_plate_temp_initial_layer", "hot_plate_temp_initial_layer"}; - bool support_multi_bed_types = std::find(bed_temp_keys.begin(), bed_temp_keys.end(), bed_temp_1st_layer_key) == - bed_temp_keys.end() || - is_BBL_printer || cfg.opt_bool("support_multi_bed_types"); + auto current_bed_has_temp = ToggleExpr(std::find(bed_temp_keys.begin(), bed_temp_keys.end(), bed_temp_1st_layer_key) == + bed_temp_keys.end(), + bed_temp_1st_layer_key).set_postfixes(" not set", " is set"); + auto support_multi_bed_types_opt = ToggleExpr::FromConfigBool(&cfg, "support_multi_bed_types"); + auto support_multi_bed_types = (has_bed_type && current_bed_has_temp) || + is_BBL_printer || support_multi_bed_types_opt; for (const auto& key : bed_temp_keys) { - toggle_line(key, support_multi_bed_types || bed_temp_1st_layer_key == key); + toggle_line(key, support_multi_bed_types || ToggleExpr(bed_temp_1st_layer_key == key, "")); } @@ -4204,20 +4242,20 @@ void TabFilament::toggle_options() // If adaptive PA is not enabled, hide the adaptive PA model section toggle_option("adaptive_pressure_advance", pa); toggle_option("adaptive_pressure_advance_overhangs", pa); - bool has_adaptive_pa = m_config->opt_bool("adaptive_pressure_advance", 0); + auto has_adaptive_pa = ToggleExpr::FromConfigBool(m_config, "adaptive_pressure_advance", 0); toggle_line("adaptive_pressure_advance_overhangs", has_adaptive_pa && pa); toggle_line("adaptive_pressure_advance_model", has_adaptive_pa && pa); toggle_line("adaptive_pressure_advance_bridges", has_adaptive_pa && pa); - bool is_pellet_printer = cfg.opt_bool("pellet_modded_printer"); + auto is_pellet_printer = ToggleExpr::FromConfigBool(&cfg, "pellet_modded_printer"); toggle_line("pellet_flow_coefficient", is_pellet_printer); toggle_line("filament_diameter", !is_pellet_printer); - bool support_chamber_temp_control = this->m_preset_bundle->printers.get_edited_preset().config.opt_bool("support_chamber_temp_control"); + auto support_chamber_temp_control = ToggleExpr::FromConfigBool(&cfg, "support_chamber_temp_control"); toggle_line("chamber_temperature", support_chamber_temp_control); std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", 0u); - bool enable_fit = volumetric_speed_cos != "0 0 0 0 0 0"; + auto enable_fit = ToggleExpr(volumetric_speed_cos != "0 0 0 0 0 0", "volumetric_speed_coefficients").set_postfixes(" != \"0 0 0 0 0 0\"", " == \"0 0 0 0 0 0\""); toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + 0u); } @@ -4231,13 +4269,14 @@ void TabFilament::toggle_options() "filament_cooling_initial_speed", "filament_cooling_final_speed"}) toggle_option(el, !is_BBL_printer); - bool multitool_ramming = m_config->opt_bool("filament_multitool_ramming", 0); + auto multitool_ramming = ToggleExpr::FromConfigBool(m_config, "filament_multitool_ramming", 0); toggle_option("filament_multitool_ramming_volume", multitool_ramming); toggle_option("filament_multitool_ramming_flow", multitool_ramming); const int extruder_idx = 0; // m_variant_combo->GetSelection(); // TODO: Orca hack toggle_line("long_retractions_when_ec", is_multi_extruder && is_BBL_printer, 256 + extruder_idx); - toggle_line("retraction_distances_when_ec", is_multi_extruder && is_BBL_printer && m_config->opt_bool("long_retractions_when_ec", extruder_idx), 256 + extruder_idx); + auto long_retractions_ec = ToggleExpr::FromConfigBool(m_config, "long_retractions_when_ec", extruder_idx); + toggle_line("retraction_distances_when_ec", is_multi_extruder && is_BBL_printer && long_retractions_ec, 256 + extruder_idx); } } @@ -5174,21 +5213,28 @@ void TabPrinter::toggle_options() }; //BBS: whether the preset is Bambu Lab printer - bool is_BBL_printer = false; + bool b_is_BBL_printer = false; if (m_preset_bundle) { - is_BBL_printer = wxGetApp().preset_bundle->is_bbl_vendor(); + b_is_BBL_printer = wxGetApp().preset_bundle->is_bbl_vendor(); } - bool is_QIDI_printer = false; + bool b_is_QIDI_printer = false; if (m_preset_bundle) { - is_QIDI_printer = wxGetApp().preset_bundle->is_qidi_vendor(); + b_is_QIDI_printer = wxGetApp().preset_bundle->is_qidi_vendor(); } - bool have_multiple_extruders = true; + auto is_BBL_printer = ToggleExpr(b_is_BBL_printer, "BBL printer").set_prefixes("Isn't ", "Is ").disable_postfix(); + auto is_QIDI_printer = ToggleExpr(b_is_QIDI_printer, "QIDI printer").set_prefixes("Isn't ", "Is ").disable_postfix(); + + // bool have_multiple_extruders = true; //m_extruders_count > 1; //if (m_active_page->title() == "Custom G-code") { // toggle_option("change_filament_gcode", have_multiple_extruders); //} + auto gcf = m_config->option>("gcode_flavor")->value; + auto gcf_is_marlin = ToggleExpr(gcf == gcfMarlinFirmware, "gcode_flavor").set_comparison(CompareType::EQ, "Marlin 2"); + auto gcf_is_marlin_legacy = ToggleExpr(gcf == gcfMarlinLegacy, "gcode_flavor").set_comparison(CompareType::EQ, "Marlin (Legacy)"); + auto gcf_is_klipper = ToggleExpr(gcf == gcfKlipper, "gcode_flavor").set_comparison(CompareType::EQ, "Klipper"); if (m_active_page->title() == L("Basic information")) { // SoftFever: hide BBL specific settings @@ -5199,15 +5245,14 @@ void TabPrinter::toggle_options() for (auto el : {"use_firmware_retraction", "use_relative_e_distances", "support_multi_bed_types", "pellet_modded_printer", "bed_mesh_max", "bed_mesh_min", "bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "thumbnails"}) toggle_line(el, !is_BBL_printer); - auto gcf = m_config->option>("gcode_flavor")->value; - toggle_line("enable_power_loss_recovery", is_BBL_printer || gcf == gcfMarlinFirmware); + toggle_line("enable_power_loss_recovery", is_BBL_printer || gcf_is_marlin); } if (m_active_page->title() == L("Machine G-code")) { PresetBundle *preset_bundle = wxGetApp().preset_bundle; std::string printer_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); - toggle_line("wrapping_detection_gcode", DevPrinterConfigUtil::support_wrapping_detection(printer_type)); + toggle_line("wrapping_detection_gcode", ToggleExpr(DevPrinterConfigUtil::support_wrapping_detection(printer_type), "Doesn't support wrapping detection").disable_postfix()); } if (m_active_page->title() == L("Multimaterial")) { @@ -5222,8 +5267,8 @@ void TabPrinter::toggle_options() }) toggle_option(el, !is_BBL_printer && !is_QIDI_printer); - auto bSEMM = m_config->opt_bool("single_extruder_multi_material"); - if (!bSEMM && m_config->opt_bool("manual_filament_change")) { + auto bSEMM = ToggleExpr::FromConfigBool(m_config, "single_extruder_multi_material"); + if ((!bSEMM && ToggleExpr::FromConfigBool(m_config, "manual_filament_change")).get_value()) { DynamicPrintConfig new_conf = *m_config; new_conf.set_key_value("manual_filament_change", new ConfigOptionBool(false)); load_config(new_conf); @@ -5240,15 +5285,17 @@ void TabPrinter::toggle_options() { size_t i = size_t(val - 1); int variant_index = get_index_for_extruder(i); - bool have_retract_length = m_config->opt_float("retraction_length", variant_index) > 0; + auto have_retract_length = ToggleExpr::FromConfigFloat(m_config, "retraction_length", CompareType::GT, 0, variant_index); - toggle_option("extruder_printable_area", false, i); // disable - toggle_line("extruder_printable_area", m_preset_bundle->get_printer_extruder_count() == 2, i); //hide - toggle_option("extruder_printable_height", false, i); - toggle_line("extruder_printable_height", m_preset_bundle->get_printer_extruder_count() == 2, i); + auto hardcoded_disabled = ToggleExpr(false, "Disabled").disable_postfix(); + auto two_extruders = ToggleExpr(m_preset_bundle->get_printer_extruder_count() == 2, "Printer extruder count").set_comparison(CompareType::EQ, "2"); + toggle_option("extruder_printable_area", hardcoded_disabled, i); // disable + toggle_line("extruder_printable_area", two_extruders, i); //hide + toggle_option("extruder_printable_height", hardcoded_disabled, i); + toggle_line("extruder_printable_height", two_extruders, i); // when using firmware retraction, firmware decides retraction length - bool use_firmware_retraction = m_config->opt_bool("use_firmware_retraction"); + auto use_firmware_retraction = ToggleExpr(m_config->opt_bool("use_firmware_retraction"), "use_firmware_retraction"); toggle_option("retract_length", !use_firmware_retraction, i); // user can customize travel length if we have retraction length or we"re using @@ -5257,7 +5304,7 @@ void TabPrinter::toggle_options() // user can customize other retraction options if retraction is enabled //BBS - bool retraction = have_retract_length || use_firmware_retraction; + auto retraction = have_retract_length || use_firmware_retraction; std::vector vec = {"z_hop", "retract_when_changing_layer"}; for (auto el : vec) toggle_option(el, retraction, i); @@ -5266,7 +5313,7 @@ void TabPrinter::toggle_options() vec.resize(0); vec = {"retract_lift_above", "retract_lift_below", "retract_lift_enforce"}; for (auto el : vec) - toggle_option(el, retraction && (m_config->opt_float("z_hop", i) > 0), i); + toggle_option(el, retraction && ToggleExpr::FromConfigFloat(m_config, "z_hop", CompareType::GT, 0, i), i); // some options only apply when not using firmware retraction vec.resize(0); @@ -5277,10 +5324,10 @@ void TabPrinter::toggle_options() //BBS toggle_option(el, retraction && !use_firmware_retraction, i); - bool wipe = retraction && m_config->opt_bool("wipe", variant_index); + auto wipe = retraction && ToggleExpr::FromConfigBool(m_config, "wipe", variant_index); toggle_option("retract_before_wipe", wipe, i); - if (use_firmware_retraction && wipe) { + if ((use_firmware_retraction && wipe).get_value()) { //wxMessageDialog dialog(parent(), MessageDialog dialog(parent(), _(L("The Wipe option is not available when using the Firmware Retraction mode.\n" @@ -5302,37 +5349,39 @@ void TabPrinter::toggle_options() // BBS toggle_option("wipe_distance", wipe, i); - toggle_option("retract_length_toolchange", have_multiple_extruders, i); + // toggle_option("retract_length_toolchange", have_multiple_extruders, i); - bool toolchange_retraction = m_config->opt_float("retract_length_toolchange", variant_index) > 0; - toggle_option("retract_restart_extra_toolchange", have_multiple_extruders && toolchange_retraction, i); + auto toolchange_retraction = ToggleExpr::FromConfigFloat(m_config, "retract_length_toolchange", CompareType::GT, 0, variant_index); + toggle_option("retract_restart_extra_toolchange", toolchange_retraction, i); - toggle_option("long_retractions_when_cut", !use_firmware_retraction && m_config->opt_int("enable_long_retraction_when_cut"), i); - toggle_line("retraction_distances_when_cut", m_config->opt_bool("long_retractions_when_cut", variant_index), i); + toggle_option("long_retractions_when_cut", + !use_firmware_retraction && ToggleExpr::FromConfigInt(m_config, "enable_long_retraction_when_cut", CompareType::NEQ, 0), i); + toggle_line("retraction_distances_when_cut", ToggleExpr::FromConfigBool(m_config, "long_retractions_when_cut", variant_index), i); - toggle_option("travel_slope", m_config->opt_enum("z_hop_types", i) != ZHopType::zhtNormal, i); + toggle_option("travel_slope", + ToggleExpr(m_config->opt_enum("z_hop_types", i) != ZHopType::zhtNormal, "z_hop_types").set_comparison(CompareType::NEQ, "Normal"), + i); } if (m_active_page->title() == L("Motion ability")) { - auto gcf = m_config->option>("gcode_flavor")->value; bool silent_mode = m_config->opt_bool("silent_mode"); int max_field = silent_mode ? 2 : 1; for (int i = 0; i < max_field; ++i) - toggle_option("machine_max_acceleration_travel", gcf != gcfMarlinLegacy && gcf != gcfKlipper, i); - toggle_line("machine_max_acceleration_travel", gcf != gcfMarlinLegacy && gcf != gcfKlipper); + toggle_option("machine_max_acceleration_travel", !gcf_is_marlin_legacy && !gcf_is_klipper, i); + toggle_line("machine_max_acceleration_travel", !gcf_is_marlin_legacy && !gcf_is_klipper); for (int i = 0; i < max_field; ++i) - toggle_option("machine_max_junction_deviation", gcf == gcfMarlinFirmware, i); - toggle_line("machine_max_junction_deviation", gcf == gcfMarlinFirmware); + toggle_option("machine_max_junction_deviation", gcf_is_marlin, i); + toggle_line("machine_max_junction_deviation", gcf_is_marlin); // Check if junction deviation value is non-zero and firmware is Marlin - bool enable_jerk = gcf != gcfMarlinFirmware; + auto enable_jerk = !gcf_is_marlin; if (gcf == gcfMarlinFirmware) { const auto *junction_deviation = m_config->option("machine_max_junction_deviation"); if (junction_deviation != nullptr) { const auto &values = junction_deviation->values; - enable_jerk = std::all_of(values.begin(), values.end(), [](double val) { return val == 0.0; }); + enable_jerk = ToggleExpr(std::all_of(values.begin(), values.end(), [](double val) { return val == 0.0; }), "machine_max_junction_deviation").set_comparison(CompareType::EQ, "0"); } else { - enable_jerk = true; + enable_jerk = ToggleExpr(true, ""); } } for (int i = 0; i < max_field; ++i) { @@ -5342,7 +5391,7 @@ void TabPrinter::toggle_options() toggle_option("machine_max_jerk_e", enable_jerk, i); } - bool resonance_avoidance = m_config->opt_bool("resonance_avoidance"); + auto resonance_avoidance = ToggleExpr::FromConfigBool(m_config, "resonance_avoidance"); toggle_option("min_resonance_avoidance_speed", resonance_avoidance); toggle_option("max_resonance_avoidance_speed", resonance_avoidance); } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 96b35b26e1..eca3a497a5 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -50,6 +51,7 @@ class ModelConfig; class ObjectBase; namespace GUI { +class ToggleExpr; class TabPresetComboBox; class OG_CustomCtrl; @@ -387,9 +389,16 @@ public: std::pair get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index = -1); Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); + + [[deprecated]] void toggle_option(const std::string &opt_key, bool toggle, int opt_index = -1); + void toggle_option(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); + + [[deprecated]] void toggle_line(const std::string &opt_key, bool toggle, int opt_index = -1); // BBS: hide some line - wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); + void toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); + + wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); bool current_preset_is_dirty() const; bool saved_preset_is_dirty() const; void update_saved_preset_from_current_preset(); diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp new file mode 100644 index 0000000000..249f5ab7dd --- /dev/null +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -0,0 +1,101 @@ + +#include "ToggleExpr.hpp" + +namespace Slic3r { namespace GUI { + +void ToggleExpr::get_reasons(std::vector& reasons, const ToggleExpr& expr) +{ + auto& expr_reasons = expr.m_reasons; + if (expr_reasons.empty()) + return; + if (expr.m_unformatted_reason) { + // If the reason is unformatted, there should only be one reason in the list, + // but there can be cases where there is a single formatted reason passed from a different ToggleExpr + assert(expr_reasons.size() == 1); + reasons.emplace_back(expr.get_prefix() + expr_reasons[0] + expr.get_postfix()); + return; + } + + reasons.insert(reasons.end(), expr_reasons.begin(), expr_reasons.end()); +} +std::string ToggleExpr::get_prefix() const +{ + if (!m_has_prefixes) + return ""; + std::string p = m_is_not ? m_inverted_prefix : m_standard_prefix; + if (!p.empty() && p.back() != ' ') + p += " "; + return p; +} + +std::string ToggleExpr::get_postfix() const +{ + if (m_disable_postfix) + return ""; + if (m_comparison_val.empty() && m_comp_type != CompareType::NO_CT) { + return " " + comparison_type_to_string(m_comp_type, !m_is_not) + " " + m_comparison_val; + } + std::string p = m_is_not ? m_inverted_postfix : m_standard_postfix; + if (!p.empty() && p.front() != ' ') + p.insert(p.begin(), ' '); + return p; +} + +template bool ToggleExpr::compare(T value, CompareType comp_type, T comp_value) +{ + switch (comp_type) { + case CompareType::NO_CT: throw std::invalid_argument("ToggleExpr::FromConfig cannot accept a condition type of NO_CT"); + case CompareType::GT: return value > comp_value; + case CompareType::LT: return value < comp_value; + case CompareType::GTE: return value >= comp_value; + case CompareType::LTE: return value <= comp_value; + case CompareType::EQ: return value == comp_value; + case CompareType::NEQ: return value != comp_value; + } + return false; +} + +std::string ToggleExpr::comparison_type_to_string(CompareType type, bool inverted) +{ + if (!inverted) { + switch (type) { + case CompareType::GT: return ">"; + case CompareType::LT: return "<"; + case CompareType::GTE: return ">="; + case CompareType::LTE: return "<="; + case CompareType::EQ: return "=="; + case CompareType::NEQ: return "!="; + default: return ""; + } + } else { + switch (type) { + case CompareType::GT: return "<="; + case CompareType::LT: return ">="; + case CompareType::GTE: return "<"; + case CompareType::LTE: return ">"; + case CompareType::EQ: return "!="; + case CompareType::NEQ: return "=="; + default: return ""; + } + } +} +ToggleExpr ToggleExpr::FromConfigBool(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) +{ + auto val = opt_idx == -1 ? config->opt_bool(opt_key) : config->opt_bool(opt_key, opt_idx); + return {val, opt_key}; +} + +ToggleExpr ToggleExpr::FromConfigInt( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, int comp_val, unsigned opt_idx) +{ + auto val = opt_idx == -1 ? config->opt_int(opt_key) : config->opt_int(opt_key, opt_idx); + return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, std::to_string(comp_val)); +} + +ToggleExpr ToggleExpr::FromConfigFloat( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, double comp_val, unsigned opt_idx) +{ + auto val = opt_idx == -1 ? config->opt_float(opt_key) : config->opt_float(opt_key, opt_idx); + return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, std::to_string(comp_val)); +} +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/ToggleExpr.hpp b/src/slic3r/GUI/ToggleExpr.hpp new file mode 100644 index 0000000000..9e6f98714d --- /dev/null +++ b/src/slic3r/GUI/ToggleExpr.hpp @@ -0,0 +1,138 @@ + +#ifndef ORCASLICER_TOGGLEEXPR_HPP +#define ORCASLICER_TOGGLEEXPR_HPP + +namespace Slic3r::GUI { + +enum class CompareType { NO_CT, GT, LT, GTE, LTE, EQ, NEQ }; + +class ToggleExpr +{ + bool m_value{}; + bool m_is_not{}; + + // If the value indicates toggle off, these are the reasons why + std::vector m_reasons{}; + bool m_unformatted_reason{false}; + + bool m_has_prefixes{}; + std::string m_standard_prefix{}; + std::string m_inverted_prefix{}; + + bool m_disable_postfix{}; + std::string m_standard_postfix{" disabled"}; + std::string m_inverted_postfix{" enabled"}; + + CompareType m_comp_type{CompareType::NO_CT}; + std::string m_comparison_val{}; + + ToggleExpr(bool value, std::vector reasons) : m_reasons(std::move(reasons)) {} + + static void get_reasons(std::vector& reasons, const ToggleExpr& expr); + +public: + ToggleExpr(bool value, std::string name) : m_value(value) + { + if (!name.empty()) { + m_unformatted_reason = true; + m_reasons = {std::move(name)}; + } + } + + friend ToggleExpr operator&&(const ToggleExpr& lhs, const ToggleExpr& rhs) + { + if (lhs.m_value && rhs.m_value) + return {true, ""}; + + std::vector reasons; + if (!lhs.m_value) + get_reasons(reasons, lhs); + if (!rhs.m_value) + get_reasons(reasons, rhs); + + return {false, reasons}; + } + + friend ToggleExpr operator||(const ToggleExpr& lhs, const ToggleExpr& rhs) + { + if (lhs.m_value || rhs.m_value) + return {true, ""}; + + std::vector reasons; + get_reasons(reasons, lhs); + get_reasons(reasons, rhs); + return {false, reasons}; + } + + friend ToggleExpr operator!(const ToggleExpr& rhs) + { + auto copy = rhs; + copy.m_value = !copy.m_value; + copy.m_is_not = !copy.m_is_not; + return copy; + } + + [[nodiscard]] std::string get_prefix() const; + + [[nodiscard]] std::string get_postfix() const; + + /// + /// \param value_false_prefix When the provided value is false, what should the prefix be? + /// \param opposite_prefix When the provided value is false when inverted ('!' operator), what should the prefix be? + /// \return self + ToggleExpr& set_prefixes(std::string value_false_prefix, std::string opposite_prefix) + { + m_standard_prefix = std::move(value_false_prefix); + m_inverted_prefix = std::move(opposite_prefix); + return *this; + } + + ToggleExpr& disable_postfix() + { + m_disable_postfix = true; + return *this; + } + + ToggleExpr& set_postfixes(std::string value_false_postfix, std::string opposite_postfix) + { + m_standard_postfix = std::move(value_false_postfix); + m_inverted_postfix = std::move(opposite_postfix); + return *this; + } + + /// Set the comparison that is occurring to generate the provided value + /// \return self + ToggleExpr& set_comparison(CompareType comp_type, std::string comparison_val) + { + m_comp_type = comp_type; + m_comparison_val = std::move(comparison_val); + return *this; + } + + [[nodiscard]] bool get_value() const { return m_value; } + + [[nodiscard]] std::vector get_reasons() const + { + std::vector out; + get_reasons(out, *this); + return out; + } + + template static bool compare(T value, CompareType comp_type, T comp_value); + + static std::string comparison_type_to_string(CompareType type, bool inverted); + + static ToggleExpr FromConfigBool(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx = -1); + + static ToggleExpr FromConfigInt( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, int comp_val, unsigned opt_idx = -1); + + static ToggleExpr FromConfigFloat( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, double comp_val, unsigned opt_idx = -1); + + static std::string build_reasons_string(std::string beginning_message, const std::vector& reasons); +}; + +} // namespace Slic3r::GUI + +#endif // ORCASLICER_TOGGLEEXPR_HPP From 1623fd03652e3c666a12512ff28586a05ba32e8f Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Wed, 18 Feb 2026 06:37:07 -0500 Subject: [PATCH 02/14] Show message with reasons when option is hidden/disabled --- src/slic3r/GUI/Field.cpp | 19 +++++++++++++++++++ src/slic3r/GUI/Tab.cpp | 10 ++++++++++ src/slic3r/GUI/ToggleExpr.cpp | 17 +++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 2cc3aa478e..dd4c8d0852 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -23,6 +23,7 @@ // BBS #include "Notebook.hpp" +#include "ToggleExpr.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/TextInput.hpp" #include "Widgets/SpinInput.hpp" @@ -30,6 +31,8 @@ #include "Widgets/TextCtrl.h" #include "../Utils/ColorSpaceConvert.hpp" + +#include #ifdef __WXOSX__ #define wxOSX true #else @@ -177,6 +180,22 @@ void Field::PostInitialize() evt.Skip(); }, getWindow()->GetId()); + + // Show tooltip on click + getWindow()->GetParent()->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& evt) { + evt.Skip(); + if (!getWindow()->IsShown()) return; + bool hit = getWindow()->GetRect().Contains(evt.GetPosition()); + // Hit check and sanity check that this is disabled + if (!hit) return; + + if (!getWindow()->IsEnabled() && !this->disabled_reasons.empty()) { + auto tooltip = wxRichToolTip("This option is disabled", + ToggleExpr::build_reasons_string("", this->disabled_reasons)); + tooltip.ShowFor(getWindow()); + } + evt.Skip(false); + }); } } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a92f4a9dd5..04bf9dcfa5 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2038,6 +2038,16 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category) evt.SetEventObject(field->getWindow()); wxPostEvent(m_page_view, evt); } + if (!field->getWindow()->IsShown()) { + Line* line = get_line(opt_key); + if (!line) return; + if (line->toggle_visible) { + show_error(this, std::string("The selected option is hidden by the config mode. ") + .append(line->get_options()[0].opt.mode == comAdvanced ? "Enable advanced mode to use this option" : "Enable developer mode to use this option")); + } else { + show_error(this, ToggleExpr::build_reasons_string("The selected option is hidden.", line->hidden_reasons)); + } + } } else if (category == "Single extruder MM setup") { // When we show and hide "Single extruder MM setup" page, diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp index 249f5ab7dd..1bef590eee 100644 --- a/src/slic3r/GUI/ToggleExpr.cpp +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -98,4 +98,21 @@ ToggleExpr ToggleExpr::FromConfigFloat( auto val = opt_idx == -1 ? config->opt_float(opt_key) : config->opt_float(opt_key, opt_idx); return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, std::to_string(comp_val)); } + +std::string ToggleExpr::build_reasons_string(std::string beginning_message, const std::vector& reasons) +{ + auto message = std::move(beginning_message); + if (!message.empty()) { + boost::trim(message); + message += " "; + } + message += "Reasons:\n"; + + for (auto& reason : reasons) { + message += reason; + message += "\n"; + } + + return message; +} }} // namespace Slic3r::GUI \ No newline at end of file From 19c87d840eac5261b03f20ae3d9822882add76f6 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Wed, 18 Feb 2026 11:39:42 -0500 Subject: [PATCH 03/14] Implement custom tooltip for disabled fields --- src/slic3r/GUI/Field.cpp | 41 ++++++++++++++++++++++++++--------- src/slic3r/GUI/Field.hpp | 8 ++++++- src/slic3r/GUI/ToggleExpr.cpp | 1 + 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index dd4c8d0852..5cbeee0286 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -182,19 +182,40 @@ void Field::PostInitialize() }, getWindow()->GetId()); // Show tooltip on click - getWindow()->GetParent()->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& evt) { + getWindow()->GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) mutable { evt.Skip(); - if (!getWindow()->IsShown()) return; - bool hit = getWindow()->GetRect().Contains(evt.GetPosition()); - // Hit check and sanity check that this is disabled - if (!hit) return; + if (!getWindow()) + return; + auto parent = getWindow()->GetParent(); + if (!parent) + return; + wxString tip = this->getWindow()->GetToolTipText(); + tip += "\n\n" + ToggleExpr::build_reasons_string("Option is disabled.", this->disabled_reasons); + tip.Trim(true); + wxRect win_rect = this->getWindow()->GetRect(); - if (!getWindow()->IsEnabled() && !this->disabled_reasons.empty()) { - auto tooltip = wxRichToolTip("This option is disabled", - ToggleExpr::build_reasons_string("", this->disabled_reasons)); - tooltip.ShowFor(getWindow()); + if (!tip.IsEmpty() && !this->getWindow()->IsEnabled() && win_rect.Contains(evt.GetPosition())) { + if (!m_tooltip && !m_tooltip_timer.IsRunning()) { + m_tooltip_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent& evt) { + if (!m_tooltip) { + m_tooltip = new wxTipWindow(this->getWindow(), tip, 300); + // Hacky way to get the underlying wxTipWindowView + auto tw_view = m_tooltip->GetChildren().front(); + tw_view->SetBackgroundColour(*wxWHITE); + m_tooltip->Refresh(); + m_tooltip->Disable(); + } + }); + m_tooltip_timer.StartOnce(300); + } + + } else { + m_tooltip_timer.Stop(); + if (m_tooltip) { + m_tooltip->Close(); + m_tooltip = nullptr; + } } - evt.Skip(false); }); } } diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index a658ffe128..4b19db7c87 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -5,6 +5,7 @@ #ifndef WX_PRECOMP #include #endif +#include #include #include @@ -289,7 +290,12 @@ protected: bool bEnterPressed = false; wxString m_na_value = _(L("N/A")); - + + // When the field is disabled, so are tooltips. + // Tooltips are manually created while the window is disabled + wxTipWindow* m_tooltip{nullptr}; + wxTimer m_tooltip_timer; + friend class OptionsGroup; }; diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp index 1bef590eee..f3dadda246 100644 --- a/src/slic3r/GUI/ToggleExpr.cpp +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -101,6 +101,7 @@ ToggleExpr ToggleExpr::FromConfigFloat( std::string ToggleExpr::build_reasons_string(std::string beginning_message, const std::vector& reasons) { + if (reasons.empty()) return ""; auto message = std::move(beginning_message); if (!message.empty()) { boost::trim(message); From c780afd1de3ec7a60912f49ca4b049b6f75e8b51 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Thu, 19 Feb 2026 06:08:53 -0500 Subject: [PATCH 04/14] Fix comparison --- src/slic3r/GUI/ToggleExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp index f3dadda246..9f39d67568 100644 --- a/src/slic3r/GUI/ToggleExpr.cpp +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -32,7 +32,7 @@ std::string ToggleExpr::get_postfix() const { if (m_disable_postfix) return ""; - if (m_comparison_val.empty() && m_comp_type != CompareType::NO_CT) { + if (!m_comparison_val.empty() && m_comp_type != CompareType::NO_CT) { return " " + comparison_type_to_string(m_comp_type, !m_is_not) + " " + m_comparison_val; } std::string p = m_is_not ? m_inverted_postfix : m_standard_postfix; From 5df5f8c3f9b0bd15772ac42ff6b45c0ae51476c8 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Thu, 19 Feb 2026 06:17:59 -0500 Subject: [PATCH 05/14] Move tooltip logic to OG control and use native tooltip --- src/slic3r/GUI/Field.cpp | 37 -------------------------------- src/slic3r/GUI/OG_CustomCtrl.cpp | 21 ++++++++++++++++++ 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 5cbeee0286..f76d35e7d1 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -180,43 +180,6 @@ void Field::PostInitialize() evt.Skip(); }, getWindow()->GetId()); - - // Show tooltip on click - getWindow()->GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& evt) mutable { - evt.Skip(); - if (!getWindow()) - return; - auto parent = getWindow()->GetParent(); - if (!parent) - return; - wxString tip = this->getWindow()->GetToolTipText(); - tip += "\n\n" + ToggleExpr::build_reasons_string("Option is disabled.", this->disabled_reasons); - tip.Trim(true); - wxRect win_rect = this->getWindow()->GetRect(); - - if (!tip.IsEmpty() && !this->getWindow()->IsEnabled() && win_rect.Contains(evt.GetPosition())) { - if (!m_tooltip && !m_tooltip_timer.IsRunning()) { - m_tooltip_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent& evt) { - if (!m_tooltip) { - m_tooltip = new wxTipWindow(this->getWindow(), tip, 300); - // Hacky way to get the underlying wxTipWindowView - auto tw_view = m_tooltip->GetChildren().front(); - tw_view->SetBackgroundColour(*wxWHITE); - m_tooltip->Refresh(); - m_tooltip->Disable(); - } - }); - m_tooltip_timer.StartOnce(300); - } - - } else { - m_tooltip_timer.Stop(); - if (m_tooltip) { - m_tooltip->Close(); - m_tooltip = nullptr; - } - } - }); } } diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 331d4a5db8..9f8817f198 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -10,6 +10,7 @@ #include #include "libslic3r/Utils.hpp" #include "I18N.hpp" +#include "ToggleExpr.hpp" #include "format.hpp" #include @@ -400,6 +401,26 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event) } if (!tooltip.IsEmpty()) break; + for (auto& ctrl_line : ctrl_lines) { + for (const auto& option : ctrl_line.og_line.get_options()) { + auto field = opt_group->get_field(option.opt_id); + if (!field) continue; + auto win = field->getWindow(); + if (!win) continue; + + auto tip = win->HasToolTips() ? win->GetToolTipText() : ""; + tip += "\n\n"; + tip += ToggleExpr::build_reasons_string("Option is disabled.", field->disabled_reasons); + tip.Trim(true); + + if (!tip.IsEmpty() && !win->IsEnabled() && is_point_in_rect(pos, win->GetRect())) { + tooltip = tip; + break; + } + } + if (!tooltip.IsEmpty()) + break; + } } // Set tooltips with information for each icon From ee1c9f4431c6207ed839b6e26be8e1ecd6672b74 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 04:07:09 -0500 Subject: [PATCH 06/14] Add more functionality to ToggleExpr --- src/slic3r/GUI/ToggleExpr.cpp | 36 +++++++----- src/slic3r/GUI/ToggleExpr.hpp | 101 +++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp index 9f39d67568..c2208ad91e 100644 --- a/src/slic3r/GUI/ToggleExpr.cpp +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -41,20 +41,6 @@ std::string ToggleExpr::get_postfix() const return p; } -template bool ToggleExpr::compare(T value, CompareType comp_type, T comp_value) -{ - switch (comp_type) { - case CompareType::NO_CT: throw std::invalid_argument("ToggleExpr::FromConfig cannot accept a condition type of NO_CT"); - case CompareType::GT: return value > comp_value; - case CompareType::LT: return value < comp_value; - case CompareType::GTE: return value >= comp_value; - case CompareType::LTE: return value <= comp_value; - case CompareType::EQ: return value == comp_value; - case CompareType::NEQ: return value != comp_value; - } - return false; -} - std::string ToggleExpr::comparison_type_to_string(CompareType type, bool inverted) { if (!inverted) { @@ -92,6 +78,11 @@ ToggleExpr ToggleExpr::FromConfigInt( return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, std::to_string(comp_val)); } +ToggleExprFragment ToggleExpr::FromConfigInt(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) +{ + return {config, opt_key, opt_idx}; +} + ToggleExpr ToggleExpr::FromConfigFloat( const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, double comp_val, unsigned opt_idx) { @@ -99,6 +90,23 @@ ToggleExpr ToggleExpr::FromConfigFloat( return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, std::to_string(comp_val)); } +ToggleExprFragment ToggleExpr::FromConfigFloat(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) +{ + return {config, opt_key, opt_idx}; +} + +ToggleExpr ToggleExpr::FromConfigString( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, const std::string& comp_val, unsigned opt_idx) +{ + auto val = opt_idx == -1 ? config->opt_string(opt_key) : config->opt_string(opt_key, opt_idx); + return ToggleExpr(compare(val, comp_type, comp_val), opt_key).set_comparison(comp_type, comp_val); +} + +ToggleExprFragment ToggleExpr::FromConfigString(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) +{ + return {config, opt_key, opt_idx}; +} + std::string ToggleExpr::build_reasons_string(std::string beginning_message, const std::vector& reasons) { if (reasons.empty()) return ""; diff --git a/src/slic3r/GUI/ToggleExpr.hpp b/src/slic3r/GUI/ToggleExpr.hpp index 9e6f98714d..fadc59332d 100644 --- a/src/slic3r/GUI/ToggleExpr.hpp +++ b/src/slic3r/GUI/ToggleExpr.hpp @@ -6,6 +6,8 @@ namespace Slic3r::GUI { enum class CompareType { NO_CT, GT, LT, GTE, LTE, EQ, NEQ }; +template class ToggleExprFragment; + class ToggleExpr { bool m_value{}; @@ -72,6 +74,22 @@ public: return copy; } + // For ToggleExpr objects that can be LHS + friend ToggleExpr& operator==(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::EQ, std::move(rhs)); } + friend ToggleExpr& operator!=(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::NEQ, std::move(rhs)); } + friend ToggleExpr& operator>(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::GT, std::move(rhs)); } + friend ToggleExpr& operator<(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::LT, std::move(rhs)); } + friend ToggleExpr& operator>=(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::GTE, std::move(rhs)); } + friend ToggleExpr& operator<=(ToggleExpr& lhs, std::string rhs) { return lhs.set_comparison(CompareType::LTE, std::move(rhs)); } + + // For newly created ToggleExpr objects + friend ToggleExpr& operator==(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::EQ, std::move(rhs)); } + friend ToggleExpr& operator!=(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::NEQ, std::move(rhs)); } + friend ToggleExpr& operator>(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::GT, std::move(rhs)); } + friend ToggleExpr& operator<(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::LT, std::move(rhs)); } + friend ToggleExpr& operator>=(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::GTE, std::move(rhs)); } + friend ToggleExpr& operator<=(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::LTE, std::move(rhs)); } + [[nodiscard]] std::string get_prefix() const; [[nodiscard]] std::string get_postfix() const; @@ -118,7 +136,19 @@ public: return out; } - template static bool compare(T value, CompareType comp_type, T comp_value); + template static bool compare(T value, CompareType comp_type, T comp_value) + { + switch (comp_type) { + case CompareType::NO_CT: throw std::invalid_argument("ToggleExpr::FromConfig cannot accept a condition type of NO_CT"); + case CompareType::GT: return value > comp_value; + case CompareType::LT: return value < comp_value; + case CompareType::GTE: return value >= comp_value; + case CompareType::LTE: return value <= comp_value; + case CompareType::EQ: return value == comp_value; + case CompareType::NEQ: return value != comp_value; + } + return false; + } static std::string comparison_type_to_string(CompareType type, bool inverted); @@ -126,13 +156,82 @@ public: static ToggleExpr FromConfigInt( const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, int comp_val, unsigned opt_idx = -1); + static ToggleExprFragment FromConfigInt(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx = -1); + + template + static ToggleExpr FromConfigEnum( + const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, EnumT comp_value, unsigned opt_idx = -1) + { + bool ret_val; + if (opt_idx == -1) { + auto enum_opt = config->option>(opt_key); + auto val = enum_opt->value; + ret_val = compare(val, comp_type, comp_value); + } else { + auto enum_opt = config->option(opt_key); + auto val = enum_opt->get_at(opt_idx); + ret_val = compare(static_cast(val), comp_type, comp_value); + } + return ToggleExpr(ret_val, opt_key).set_comparison(comp_type, ConfigOptionEnum::get_enum_names().at(static_cast(comp_value))); + } + + template + static ToggleExprFragment FromConfigEnum(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx = -1) + { return {config, opt_key, opt_idx}; } static ToggleExpr FromConfigFloat( const DynamicPrintConfig* config, const std::string& opt_key, CompareType comp_type, double comp_val, unsigned opt_idx = -1); + static ToggleExprFragment FromConfigFloat(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx = -1); + + static ToggleExpr FromConfigString(const DynamicPrintConfig* config, + const std::string& opt_key, + CompareType comp_type, + const std::string& comp_val, + unsigned opt_idx = -1); + static ToggleExprFragment FromConfigString(const DynamicPrintConfig* config, + const std::string& opt_key, + unsigned opt_idx = -1); static std::string build_reasons_string(std::string beginning_message, const std::vector& reasons); }; +template class ToggleExprFragment +{ + const DynamicPrintConfig* config; + std::string opt_key; + unsigned opt_idx; + + ToggleExpr build_expr(CompareType comp_type, T comp_val) const + { + if constexpr (std::is_same_v) { + return ToggleExpr::FromConfigInt(config, opt_key, comp_type, comp_val, opt_idx); + } else if constexpr (std::is_same_v) { + return ToggleExpr::FromConfigFloat(config, opt_key, comp_type, comp_val, opt_idx); + } else if constexpr (std::is_same_v) { + return ToggleExpr::FromConfigString(config, opt_key, comp_type, comp_val, opt_idx); + } else if constexpr (std::is_enum_v) { + return ToggleExpr::FromConfigEnum(config, opt_key, comp_type, comp_val, opt_idx); + } else { + // Unsupported + static_assert(false, "The provided type is unsupported by this class"); + } + return ToggleExpr(false, ""); + } + +public: + ToggleExprFragment(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) + : config(config), opt_key(opt_key), opt_idx(opt_idx) + {} + + friend ToggleExpr operator==(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::EQ, rhs); } + friend ToggleExpr operator!=(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::NEQ, rhs); } + friend ToggleExpr operator>(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::GT, rhs); } + friend ToggleExpr operator<(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::LT, rhs); } + friend ToggleExpr operator>=(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::GTE, rhs); } + friend ToggleExpr operator<=(const ToggleExprFragment& lhs, T rhs) { return lhs.build_expr(CompareType::LTE, rhs); } + + friend class ToggleExpr; +}; } // namespace Slic3r::GUI #endif // ORCASLICER_TOGGLEEXPR_HPP From a88c573078b6528f9c4c91350a7797a3ad763f2d Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 04:10:49 -0500 Subject: [PATCH 07/14] Update Tab to use new ToggleExpr functions --- src/slic3r/GUI/Tab.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 04bf9dcfa5..fb85702fad 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4264,8 +4264,8 @@ void TabFilament::toggle_options() auto support_chamber_temp_control = ToggleExpr::FromConfigBool(&cfg, "support_chamber_temp_control"); toggle_line("chamber_temperature", support_chamber_temp_control); - std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", 0u); - auto enable_fit = ToggleExpr(volumetric_speed_cos != "0 0 0 0 0 0", "volumetric_speed_coefficients").set_postfixes(" != \"0 0 0 0 0 0\"", " == \"0 0 0 0 0 0\""); + auto volumetric_speed_cos = ToggleExpr::FromConfigString(m_config, "volumetric_speed_coefficients", 0u); + auto enable_fit = volumetric_speed_cos != "0 0 0 0 0 0"; toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + 0u); } @@ -5241,10 +5241,10 @@ void TabPrinter::toggle_options() //if (m_active_page->title() == "Custom G-code") { // toggle_option("change_filament_gcode", have_multiple_extruders); //} - auto gcf = m_config->option>("gcode_flavor")->value; - auto gcf_is_marlin = ToggleExpr(gcf == gcfMarlinFirmware, "gcode_flavor").set_comparison(CompareType::EQ, "Marlin 2"); - auto gcf_is_marlin_legacy = ToggleExpr(gcf == gcfMarlinLegacy, "gcode_flavor").set_comparison(CompareType::EQ, "Marlin (Legacy)"); - auto gcf_is_klipper = ToggleExpr(gcf == gcfKlipper, "gcode_flavor").set_comparison(CompareType::EQ, "Klipper"); + auto gcf = ToggleExpr::FromConfigEnum(m_config, "gcode_flavor"); + auto gcf_is_marlin = gcf == gcfMarlinFirmware; + auto gcf_is_marlin_legacy = gcf == gcfMarlinLegacy; + auto gcf_is_klipper = gcf == gcfKlipper; if (m_active_page->title() == L("Basic information")) { // SoftFever: hide BBL specific settings @@ -5295,17 +5295,17 @@ void TabPrinter::toggle_options() { size_t i = size_t(val - 1); int variant_index = get_index_for_extruder(i); - auto have_retract_length = ToggleExpr::FromConfigFloat(m_config, "retraction_length", CompareType::GT, 0, variant_index); + auto have_retract_length = ToggleExpr::FromConfigFloat(m_config, "retraction_length", variant_index) > 0; auto hardcoded_disabled = ToggleExpr(false, "Disabled").disable_postfix(); - auto two_extruders = ToggleExpr(m_preset_bundle->get_printer_extruder_count() == 2, "Printer extruder count").set_comparison(CompareType::EQ, "2"); + auto two_extruders = ToggleExpr(m_preset_bundle->get_printer_extruder_count() == 2, "Printer extruder count") > "2"; toggle_option("extruder_printable_area", hardcoded_disabled, i); // disable toggle_line("extruder_printable_area", two_extruders, i); //hide toggle_option("extruder_printable_height", hardcoded_disabled, i); toggle_line("extruder_printable_height", two_extruders, i); // when using firmware retraction, firmware decides retraction length - auto use_firmware_retraction = ToggleExpr(m_config->opt_bool("use_firmware_retraction"), "use_firmware_retraction"); + auto use_firmware_retraction = ToggleExpr::FromConfigBool(m_config, "use_firmware_retraction"); toggle_option("retract_length", !use_firmware_retraction, i); // user can customize travel length if we have retraction length or we"re using @@ -5323,7 +5323,7 @@ void TabPrinter::toggle_options() vec.resize(0); vec = {"retract_lift_above", "retract_lift_below", "retract_lift_enforce"}; for (auto el : vec) - toggle_option(el, retraction && ToggleExpr::FromConfigFloat(m_config, "z_hop", CompareType::GT, 0, i), i); + toggle_option(el, retraction && ToggleExpr::FromConfigFloat(m_config, "z_hop", i) > 0, i); // some options only apply when not using firmware retraction vec.resize(0); @@ -5361,15 +5361,15 @@ void TabPrinter::toggle_options() // toggle_option("retract_length_toolchange", have_multiple_extruders, i); - auto toolchange_retraction = ToggleExpr::FromConfigFloat(m_config, "retract_length_toolchange", CompareType::GT, 0, variant_index); + auto toolchange_retraction = ToggleExpr::FromConfigFloat(m_config, "retract_length_toolchange", variant_index) > 0; toggle_option("retract_restart_extra_toolchange", toolchange_retraction, i); toggle_option("long_retractions_when_cut", - !use_firmware_retraction && ToggleExpr::FromConfigInt(m_config, "enable_long_retraction_when_cut", CompareType::NEQ, 0), i); + !use_firmware_retraction && ToggleExpr::FromConfigInt(m_config, "enable_long_retraction_when_cut") != 0, i); toggle_line("retraction_distances_when_cut", ToggleExpr::FromConfigBool(m_config, "long_retractions_when_cut", variant_index), i); toggle_option("travel_slope", - ToggleExpr(m_config->opt_enum("z_hop_types", i) != ZHopType::zhtNormal, "z_hop_types").set_comparison(CompareType::NEQ, "Normal"), + ToggleExpr::FromConfigEnum(m_config, "z_hop_types", i) != ZHopType::zhtNormal, i); } @@ -5385,11 +5385,11 @@ void TabPrinter::toggle_options() // Check if junction deviation value is non-zero and firmware is Marlin auto enable_jerk = !gcf_is_marlin; - if (gcf == gcfMarlinFirmware) { + if ((gcf == gcfMarlinFirmware).get_value()) { const auto *junction_deviation = m_config->option("machine_max_junction_deviation"); if (junction_deviation != nullptr) { const auto &values = junction_deviation->values; - enable_jerk = ToggleExpr(std::all_of(values.begin(), values.end(), [](double val) { return val == 0.0; }), "machine_max_junction_deviation").set_comparison(CompareType::EQ, "0"); + enable_jerk = ToggleExpr(std::all_of(values.begin(), values.end(), [](double val) { return val == 0.0; }), "machine_max_junction_deviation") == "0"; } else { enable_jerk = ToggleExpr(true, ""); } @@ -7441,7 +7441,7 @@ void TabSLAMaterial::reload_config() void TabSLAMaterial::toggle_options() { const Preset ¤t_printer = m_preset_bundle->printers.get_edited_preset(); - std::string model = current_printer.config.opt_string("printer_model"); + auto model = ToggleExpr::FromConfigString(¤t_printer.config, "printer_model"); m_config_manipulation.toggle_field("material_print_speed", model != "SL1"); } @@ -7644,12 +7644,12 @@ ConfigManipulation Tab::get_config_manipulation() update(); }; - auto cb_toggle_field = [this](const t_config_option_key& opt_key, bool toggle, int opt_index) { - return toggle_option(opt_key, toggle, opt_index >= 0 ? opt_index + 256 : opt_index); + auto cb_toggle_field = [this](const t_config_option_key& opt_key, const ToggleExpr& toggle_expr, int opt_index) { + return toggle_option(opt_key, toggle_expr, opt_index >= 0 ? opt_index + 256 : opt_index); }; - auto cb_toggle_line = [this](const t_config_option_key &opt_key, bool toggle, int opt_index) { - return toggle_line(opt_key, toggle, opt_index >= 0 ? opt_index + 256 : opt_index); + auto cb_toggle_line = [this](const t_config_option_key &opt_key, const ToggleExpr& toggle_expr, int opt_index) { + return toggle_line(opt_key, toggle_expr, opt_index >= 0 ? opt_index + 256 : opt_index); }; auto cb_value_change = [this](const std::string& opt_key, const boost::any& value) { From 12203b56c5bdc9fabb836d602641513d8888cbf8 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 04:16:46 -0500 Subject: [PATCH 08/14] Update ConfigManipulation and GUI_ObjectSettings to use ToggleExpr --- src/slic3r/GUI/ConfigManipulation.cpp | 245 ++++++++++----------- src/slic3r/GUI/ConfigManipulation.hpp | 13 +- src/slic3r/GUI/GUI_ObjectSettings.cpp | 4 +- src/slic3r/GUI/GUI_ObjectTableSettings.cpp | 16 +- 4 files changed, 138 insertions(+), 140 deletions(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a61477e006..52d4222f12 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -36,22 +36,22 @@ t_config_option_keys const &ConfigManipulation::applying_keys() const return m_applying_keys; } -void ConfigManipulation::toggle_field(const std::string &opt_key, const bool toggle, int opt_index /* = -1*/) +void ConfigManipulation::toggle_field(const std::string &opt_key, const ToggleExpr& toggle_expr, int opt_index /* = -1*/) { if (local_config) { if (local_config->option(opt_key) == nullptr) return; } - cb_toggle_field(opt_key, toggle, opt_index); + cb_toggle_field(opt_key, toggle_expr, opt_index); } -void ConfigManipulation::toggle_line(const std::string& opt_key, const bool toggle, int opt_index) +void ConfigManipulation::toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index) { if (local_config) { if (local_config->option(opt_key) == nullptr) return; } if (cb_toggle_line) - cb_toggle_line(opt_key, toggle, opt_index); + cb_toggle_line(opt_key, toggle_expr, opt_index); } void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) { @@ -572,81 +572,83 @@ void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std:: void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, const bool is_global_config) { PresetBundle *preset_bundle = wxGetApp().preset_bundle; + auto is_global_config_te = ToggleExpr(is_global_config, "Editing").set_postfixes("object config", "global config"); - auto gcflavor = preset_bundle->printers.get_edited_preset().config.option>("gcode_flavor")->value; + const auto gcflavor = ToggleExpr::FromConfigEnum(&preset_bundle->printers.get_edited_preset().config, "gcode_flavor"); - bool have_volumetric_extrusion_rate_slope = config->option("max_volumetric_extrusion_rate_slope")->value > 0; + auto have_volumetric_extrusion_rate_slope = ToggleExpr::FromConfigFloat(config, "max_volumetric_extrusion_rate_slope") > 0; float have_volumetric_extrusion_rate_slope_segment_length = config->option("max_volumetric_extrusion_rate_slope_segment_length")->value; toggle_field("enable_arc_fitting", !have_volumetric_extrusion_rate_slope); toggle_line("max_volumetric_extrusion_rate_slope_segment_length", have_volumetric_extrusion_rate_slope); toggle_line("extrusion_rate_smoothing_external_perimeter_only", have_volumetric_extrusion_rate_slope); - if(have_volumetric_extrusion_rate_slope) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false)); + if(have_volumetric_extrusion_rate_slope.get_value()) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false)); if(have_volumetric_extrusion_rate_slope_segment_length < 0.5) { DynamicPrintConfig new_conf = *config; new_conf.set_key_value("max_volumetric_extrusion_rate_slope_segment_length", new ConfigOptionFloat(1)); apply(config, &new_conf); } - bool have_perimeters = config->opt_int("wall_loops") > 0; + auto have_perimeters = ToggleExpr::FromConfigInt(config, "wall_loops") > 0; for (auto el : { "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall", "seam_position", "staggered_inner_seams", "wall_sequence", "outer_wall_line_width", "inner_wall_speed", "outer_wall_speed", "small_perimeter_speed", "small_perimeter_threshold" }) toggle_field(el, have_perimeters); - bool have_infill = config->option("sparse_infill_density")->value > 0; + auto have_infill = ToggleExpr::FromConfigFloat(config, "sparse_infill_density") > 0; // sparse_infill_filament uses the same logic as in Print::extruders() for (auto el : { "sparse_infill_pattern", "infill_combination", "fill_multiline","infill_direction", "minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor", "infill_anchor_max","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"}) toggle_line(el, have_infill); - bool have_combined_infill = config->opt_bool("infill_combination") && have_infill; + auto have_combined_infill = ToggleExpr::FromConfigBool(config, "infill_combination") && have_infill; toggle_line("infill_combination_max_layer_height", have_combined_infill); // Infill patterns that support multiline infill. - InfillPattern pattern = config->opt_enum("sparse_infill_pattern"); - bool have_multiline_infill_pattern = pattern == ipGyroid || pattern == ipGrid || pattern == ipRectilinear || pattern == ipTpmsD || pattern == ipTpmsFK || pattern == ipCrossHatch || pattern == ipHoneycomb || pattern == ipLateralLattice || pattern == ipLateralHoneycomb || pattern == ipConcentric || - pattern == ipCubic || pattern == ipStars || pattern == ipAlignedRectilinear || pattern == ipLightning || pattern == ip3DHoneycomb || pattern == ipAdaptiveCubic || pattern == ipSupportCubic|| pattern == ipTriangles || pattern == ipQuarterCubic|| pattern == ipArchimedeanChords || pattern == ipHilbertCurve || pattern == ipOctagramSpiral; - + auto pattern = ToggleExpr::FromConfigEnum(config, "sparse_infill_pattern"); + auto pattern_e = config->opt_enum("sparse_infill_pattern"); + auto non_multiline_infill_pattern = pattern == ipCrossHatch || pattern == ipLine || + pattern == ipLockedZag || pattern == ipMonotonic || pattern == ipMonotonicLine || pattern == ipZigZag || + ToggleExpr(pattern_e == ipConcentricInternal || pattern_e == ipSupportBase, ""); // If there is infill, enable/disable fill_multiline according to whether the pattern supports multiline infill. - if (have_infill) { - toggle_field("fill_multiline", have_multiline_infill_pattern); + if (have_infill.get_value()) { + toggle_field("fill_multiline", !non_multiline_infill_pattern); // If the infill pattern does not support multiline fill_multiline is changed to 1. // Necessary when the pattern contains params.multiline (for example, triangles because they belong to the rectilinear class) - if (!have_multiline_infill_pattern) { + if (non_multiline_infill_pattern) { DynamicPrintConfig new_conf = *config; new_conf.set_key_value("fill_multiline", new ConfigOptionInt(1)); apply(config, &new_conf); } // Hide infill anchor max if sparse_infill_pattern is not line or if sparse_infill_pattern is line but infill_anchor_max is 0. - bool infill_anchor = config->opt_enum("sparse_infill_pattern") != ipLine; + auto infill_anchor = ToggleExpr::FromConfigEnum(config, "sparse_infill_pattern") != ipLine; toggle_field("infill_anchor_max", infill_anchor); // Only allow configuration of open anchors if the anchoring is enabled. - bool has_infill_anchors = infill_anchor && config->option("infill_anchor_max")->value > 0; + auto has_infill_anchors = infill_anchor && ToggleExpr::FromConfigFloat(config, "infill_anchor_max", CompareType::GT, 0); toggle_field("infill_anchor", has_infill_anchors); } //cross zag - bool is_cross_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipCrossZag; - bool is_locked_zig = config->option>("sparse_infill_pattern")->value == InfillPattern::ipLockedZag; + auto is_cross_zag = ToggleExpr::FromConfigEnum(config, "sparse_infill_pattern") == InfillPattern::ipCrossZag; + auto is_locked_zig = ToggleExpr::FromConfigEnum(config, "sparse_infill_pattern") == InfillPattern::ipLockedZag; toggle_line("infill_shift_step", is_cross_zag || is_locked_zig); - + for (auto el : { "skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth","skin_infill_line_width", "skeleton_infill_line_width" }) toggle_line(el, is_locked_zig); - bool is_zig_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipZigZag; + auto is_zig_zag = ToggleExpr::FromConfigEnum(config, "sparse_infill_pattern") == InfillPattern::ipZigZag; toggle_line("symmetric_infill_y_axis", is_zig_zag || is_cross_zag || is_locked_zig); - bool has_spiral_vase = config->opt_bool("spiral_mode"); + auto has_spiral_vase = ToggleExpr::FromConfigBool(config, "spiral_mode"); toggle_line("spiral_mode_smooth", has_spiral_vase); - toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth")); + toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && ToggleExpr::FromConfigBool(config, "spiral_mode_smooth")); toggle_line("spiral_starting_flow_ratio", has_spiral_vase); toggle_line("spiral_finishing_flow_ratio", has_spiral_vase); - bool has_top_shell = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1); - bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0; - bool has_solid_infill = has_top_shell || has_bottom_shell; + auto has_top_shell = ToggleExpr::FromConfigInt(config, "top_shell_layers") > 0 || (has_spiral_vase && ToggleExpr::FromConfigInt(config, "bottom_shell_layers") > 1); + auto has_bottom_shell = ToggleExpr::FromConfigInt(config, "bottom_shell_layers") > 0; + auto has_solid_infill = has_top_shell || has_bottom_shell; toggle_field("top_surface_pattern", has_top_shell); toggle_field("bottom_surface_pattern", has_bottom_shell); toggle_field("top_surface_density", has_top_shell); @@ -669,68 +671,61 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : { "top_surface_line_width", "top_surface_speed" }) toggle_field(el, has_top_shell); - bool have_default_acceleration = config->opt_float("default_acceleration") > 0; + auto have_default_acceleration = ToggleExpr::FromConfigFloat(config, "default_acceleration") > 0; for (auto el : {"outer_wall_acceleration", "inner_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "travel_acceleration", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration"}) toggle_field(el, have_default_acceleration); - bool machine_supports_junction_deviation = false; - if (gcflavor == gcfMarlinFirmware) { + auto machine_supports_junction_deviation = gcflavor == gcfMarlinFirmware; + if (machine_supports_junction_deviation.get_value()) { if (const auto *machine_jd = preset_bundle->printers.get_edited_preset().config.option("machine_max_junction_deviation")) { - machine_supports_junction_deviation = !machine_jd->values.empty() && machine_jd->values.front() > 0.0; + machine_supports_junction_deviation = ToggleExpr(!machine_jd->values.empty(), "machine_max_junction_deviation is not set").disable_postfix() && ToggleExpr(machine_jd->values.front() > 0.0, "machine_max_junction_deviation[0]") > 0; } } - toggle_line("default_junction_deviation", gcflavor == gcfMarlinFirmware); - if (machine_supports_junction_deviation) { - toggle_field("default_junction_deviation", true); - toggle_field("default_jerk", false); - for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk", "travel_jerk", "infill_jerk"}) - toggle_line(el, false); - } else { - toggle_field("default_junction_deviation", false); - toggle_field("default_jerk", true); - bool have_default_jerk = config->has("default_jerk") && config->opt_float("default_jerk") > 0; - for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk", "travel_jerk", "infill_jerk"}) { - toggle_line(el, true); + toggle_field("default_junction_deviation", machine_supports_junction_deviation); + toggle_field("default_jerk", !machine_supports_junction_deviation); + ToggleExpr have_default_jerk = ToggleExpr(config->has("default_jerk") && config->opt_float("default_jerk") > 0, "default_jerk") > "0"; + for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk", "travel_jerk", "infill_jerk"}) { + toggle_line(el, !machine_supports_junction_deviation); + if (machine_supports_junction_deviation.get_value()) toggle_field(el, have_default_jerk); - } } - bool have_skirt = config->opt_int("skirt_loops") > 0; - toggle_field("skirt_height", have_skirt && config->opt_enum("draft_shield") != dsEnabled); + auto have_skirt = ToggleExpr::FromConfigInt(config, "skirt_loops") > 0; + toggle_field("skirt_height", have_skirt && ToggleExpr::FromConfigEnum(config, "draft_shield") != dsEnabled); toggle_line("single_loop_draft_shield", have_skirt); // ORCA: Display one wall if skirt enabled for (auto el : {"skirt_type", "min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_speed", "draft_shield"}) toggle_field(el, have_skirt); - bool have_brim = (config->opt_enum("brim_type") != btNoBrim); + auto have_brim = ToggleExpr::FromConfigEnum(config, "brim_type") != btNoBrim; toggle_field("brim_object_gap", have_brim); toggle_field("brim_use_efc_outline", have_brim); - bool have_brim_width = (config->opt_enum("brim_type") != btNoBrim) && config->opt_enum("brim_type") != btAutoBrim && - config->opt_enum("brim_type") != btPainted; + auto have_brim_width = ToggleExpr::FromConfigEnum(config, "brim_type") != btNoBrim && ToggleExpr::FromConfigEnum(config, "brim_type") != btAutoBrim && + ToggleExpr::FromConfigEnum(config, "brim_type") != btPainted; toggle_field("brim_width", have_brim_width); // wall_filament uses the same logic as in Print::extruders() toggle_field("wall_filament", have_perimeters || have_brim); - bool have_brim_ear = (config->opt_enum("brim_type") == btEar); - const auto brim_width = config->opt_float("brim_width"); + auto have_brim_ear = ToggleExpr::FromConfigEnum(config, "brim_type") == btEar; + auto brim_width_valid = ToggleExpr::FromConfigFloat(config, "brim_width") > 0.0f; // disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0 - toggle_field("brim_ears_max_angle", brim_width > 0.0f); - toggle_field("brim_ears_detection_length", brim_width > 0.0f); + toggle_field("brim_ears_max_angle", brim_width_valid); + toggle_field("brim_ears_detection_length", brim_width_valid); // hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected toggle_line("brim_ears_max_angle", have_brim_ear); toggle_line("brim_ears_detection_length", have_brim_ear); // Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled - toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0); + toggle_line("elefant_foot_compensation_layers", ToggleExpr::FromConfigFloat(config, "elefant_foot_compensation") > 0); - bool have_raft = config->opt_int("raft_layers") > 0; - bool have_support_material = config->opt_bool("enable_support") || have_raft; + auto have_raft = ToggleExpr::FromConfigInt(config, "raft_layers") > 0; + auto have_support_material = ToggleExpr::FromConfigBool(config, "enable_support") || have_raft; SupportType support_type = config->opt_enum("support_type"); - bool have_support_interface = config->opt_int("support_interface_top_layers") > 0 || config->opt_int("support_interface_bottom_layers") > 0; - bool have_support_soluble = have_support_material && config->opt_float("support_top_z_distance") == 0; - auto support_style = config->opt_enum("support_style"); + auto have_support_interface = ToggleExpr::FromConfigInt(config, "support_interface_top_layers") > 0 || ToggleExpr::FromConfigInt(config, "support_interface_bottom_layers") > 0; + auto have_support_soluble = have_support_material && ToggleExpr::FromConfigFloat(config, "support_top_z_distance") == 0; + const auto support_style = ToggleExpr::FromConfigEnum(config, "support_style"); for (auto el : { "support_style", "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_angle", "support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers", @@ -738,15 +733,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co "support_type", "support_on_build_plate_only", "support_critical_regions_only", "support_interface_not_for_body", "support_object_xy_distance", "support_object_first_layer_gap", "independent_support_layer_height"}) toggle_field(el, have_support_material); - toggle_field("support_threshold_angle", have_support_material && is_auto(support_type)); - toggle_field("support_threshold_overlap", config->opt_int("support_threshold_angle") == 0 && have_support_material && is_auto(support_type)); + auto is_auto_tree = ToggleExpr(is_auto(support_type), "Is not auto support type").disable_postfix(); + toggle_field("support_threshold_angle", have_support_material && is_auto_tree); + toggle_field("support_threshold_overlap", ToggleExpr::FromConfigInt(config, "support_threshold_angle") == 0 && have_support_material && is_auto_tree); //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); - bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type); - bool support_is_normal_tree = support_is_tree && support_style != smsTreeOrganic && + auto support_is_tree = ToggleExpr::FromConfigBool(config, "enable_support") && ToggleExpr(is_tree(support_type), "Is not tree support type").disable_postfix(); + auto support_is_normal_tree = support_is_tree && support_style != smsTreeOrganic && // Orca: use organic as default support_style != smsDefault; - bool support_is_organic = support_is_tree && !support_is_normal_tree; + auto support_is_organic = support_is_tree && !support_is_normal_tree; // settings shared by normal and organic trees for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" }) toggle_line(el, support_is_normal_tree); @@ -757,19 +753,19 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_angle_slow", "tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle"}) toggle_line(el, support_is_organic); - toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim")); + toggle_field("tree_support_brim_width", support_is_tree && !ToggleExpr::FromConfigBool(config, "tree_support_auto_brim")); // tree support use max_bridge_length instead of bridge_no_support toggle_line("max_bridge_length", support_is_tree); toggle_line("bridge_no_support", !support_is_tree); - toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_tree); + toggle_line("support_critical_regions_only", is_auto_tree && support_is_tree); for (auto el : { "support_interface_filament", "support_interface_loop_pattern", "support_bottom_interface_spacing" }) toggle_field(el, have_support_material && have_support_interface); - bool can_ironing_support = have_raft || (have_support_material && config->opt_int("support_interface_top_layers") > 0); + auto can_ironing_support = have_raft || (have_support_material && ToggleExpr::FromConfigInt(config, "support_interface_top_layers") > 0); toggle_field("support_ironing", can_ironing_support); - bool has_support_ironing = can_ironing_support && config->opt_bool("support_ironing"); + auto has_support_ironing = can_ironing_support && ToggleExpr::FromConfigBool(config, "support_ironing"); for (auto el : {"support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing" }) toggle_line(el, has_support_ironing); // Orca: Force solid support interface when using support ironing @@ -793,134 +789,135 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : { "raft_first_layer_expansion", "raft_first_layer_density"}) toggle_field(el, have_support_material && !(support_is_normal_tree && !have_raft)); - bool has_ironing = (config->opt_enum("ironing_type") != IroningType::NoIroning); + auto has_ironing = ToggleExpr::FromConfigEnum(config, "ironing_type") != IroningType::NoIroning; for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_angle", "ironing_inset", "ironing_angle_fixed" }) toggle_line(el, has_ironing); - + toggle_line("ironing_speed", has_ironing || has_support_ironing); - bool have_sequential_printing = (config->opt_enum("print_sequence") == PrintSequence::ByObject); + auto have_sequential_printing = ToggleExpr::FromConfigEnum(config, "print_sequence") == PrintSequence::ByObject; // for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) // toggle_field(el, have_sequential_printing); toggle_field("print_order", !have_sequential_printing); - toggle_field("single_extruder_multi_material", !is_BBL_Printer); + auto is_BBL_printer = ToggleExpr(is_BBL_Printer, "BBL printer").set_prefixes("Isn't", "Is").disable_postfix(); + toggle_field("single_extruder_multi_material", !is_BBL_printer); - auto bSEMM = preset_bundle->printers.get_edited_preset().config.opt_bool("single_extruder_multi_material"); + auto bSEMM = ToggleExpr::FromConfigBool(&preset_bundle->printers.get_edited_preset().config, "single_extruder_multi_material"); toggle_field("ooze_prevention", !bSEMM); - bool have_ooze_prevention = config->opt_bool("ooze_prevention"); + auto have_ooze_prevention = ToggleExpr::FromConfigBool(config, "ooze_prevention"); toggle_line("standby_temperature_delta", have_ooze_prevention); toggle_line("preheat_time", have_ooze_prevention); - int preheat_steps = config->opt_int("preheat_steps"); + const auto preheat_steps = ToggleExpr::FromConfigInt(config, "preheat_steps"); toggle_line("preheat_steps", have_ooze_prevention && (preheat_steps > 0)); - bool have_prime_tower = config->opt_bool("enable_prime_tower"); + auto have_prime_tower = ToggleExpr::FromConfigBool(config, "enable_prime_tower"); for (auto el : {"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "wipe_tower_wall_type", "prime_tower_infill_gap","prime_tower_enable_framework"}) toggle_line(el, have_prime_tower); for (auto el : {"wall_filament", "sparse_infill_filament", "solid_infill_filament", "wipe_tower_filament"}) toggle_line(el, !bSEMM); - bool purge_in_primetower = preset_bundle->printers.get_edited_preset().config.opt_bool("purge_in_prime_tower"); + auto purge_in_primetower = ToggleExpr::FromConfigBool(&preset_bundle->printers.get_edited_preset().config, "purge_in_prime_tower"); for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_max_purge_speed", "wipe_tower_bridging", "wipe_tower_extra_flow", "wipe_tower_no_sparse_layers"}) - toggle_line(el, have_prime_tower && !is_BBL_Printer); + toggle_line(el, have_prime_tower && !is_BBL_printer); - WipeTowerWallType wipe_tower_wall_type = config->opt_enum("wipe_tower_wall_type"); - bool have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower; - toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone); + const auto wipe_tower_wall_type = ToggleExpr::FromConfigEnum(config,"wipe_tower_wall_type"); + auto have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower; + toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone); toggle_line("wipe_tower_extra_rib_length", have_rib_wall); toggle_line("wipe_tower_rib_width", have_rib_wall); toggle_line("wipe_tower_fillet_wall", have_rib_wall); - toggle_field("prime_tower_width", have_prime_tower && !(is_BBL_Printer && have_rib_wall)); + toggle_field("prime_tower_width", have_prime_tower && !(is_BBL_printer && have_rib_wall)); - toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && !is_BBL_Printer); + toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && !is_BBL_printer); toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM)); for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"}) toggle_field(el, have_prime_tower); - bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall"); + auto have_avoid_crossing_perimeters = ToggleExpr::FromConfigBool(config, "reduce_crossing_wall"); toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters); - bool has_set_other_flow_ratios = config->opt_bool("set_other_flow_ratios"); + auto has_set_other_flow_ratios = ToggleExpr::FromConfigBool(config, "set_other_flow_ratios"); for (auto el : {"first_layer_flow_ratio", "outer_wall_flow_ratio", "inner_wall_flow_ratio", "overhang_flow_ratio", "sparse_infill_flow_ratio", "internal_solid_infill_flow_ratio", "gap_fill_flow_ratio", "support_flow_ratio", "support_interface_flow_ratio"}) toggle_line(el, has_set_other_flow_ratios); - bool has_overhang_speed = config->opt_bool("enable_overhang_speed"); + auto has_overhang_speed = ToggleExpr::FromConfigBool(config, "enable_overhang_speed"); for (auto el : {"overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"}) toggle_line(el, has_overhang_speed); toggle_line("slowdown_for_curled_perimeters", has_overhang_speed); - toggle_line("flush_into_objects", !is_global_config); + toggle_line("flush_into_objects", !is_global_config_te); - toggle_line("support_interface_not_for_body",config->opt_int("support_interface_filament")&&!config->opt_int("support_filament")); + toggle_line("support_interface_not_for_body",ToggleExpr::FromConfigInt(config, "support_interface_filament") != 0 && ToggleExpr::FromConfigInt(config, "support_filament") == 0); // Get the current fuzzy skin state - bool has_fuzzy_skin = config->opt_enum("fuzzy_skin") != FuzzySkinType::Disabled_fuzzy; - + auto has_fuzzy_skin = ToggleExpr::FromConfigEnum(config, "fuzzy_skin") != FuzzySkinType::Disabled_fuzzy; + // Show fuzzy skin options when fuzzy skin is not disabled for (auto el : {"fuzzy_skin_mode", "fuzzy_skin_noise_type", "fuzzy_skin_point_distance", "fuzzy_skin_thickness", "fuzzy_skin_first_layer"}) toggle_line(el, has_fuzzy_skin); - + // Show noise type specific options with the same logic - NoiseType fuzzy_skin_noise_type = config->opt_enum("fuzzy_skin_noise_type"); + const auto fuzzy_skin_noise_type = ToggleExpr::FromConfigEnum(config, "fuzzy_skin_noise_type"); toggle_line("fuzzy_skin_scale", fuzzy_skin_noise_type != NoiseType::Classic && has_fuzzy_skin); toggle_line("fuzzy_skin_octaves", fuzzy_skin_noise_type != NoiseType::Classic && fuzzy_skin_noise_type != NoiseType::Voronoi && has_fuzzy_skin); toggle_line("fuzzy_skin_persistence", (fuzzy_skin_noise_type == NoiseType::Perlin || fuzzy_skin_noise_type == NoiseType::Billow) && has_fuzzy_skin); - bool have_arachne = config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne; + auto have_arachne = ToggleExpr::FromConfigEnum(config, "wall_generator") == PerimeterGeneratorType::Arachne; for (auto el : {"wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_length_factor", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"}) toggle_line(el, have_arachne); toggle_field("detect_thin_wall", !have_arachne); // Orca - auto is_role_based_wipe_speed = config->opt_bool("role_based_wipe_speed"); + auto is_role_based_wipe_speed = ToggleExpr::FromConfigBool(config, "role_based_wipe_speed"); toggle_field("wipe_speed",!is_role_based_wipe_speed); for (auto el : {"accel_to_decel_enable", "accel_to_decel_factor"}) toggle_line(el, gcflavor == gcfKlipper); - if(gcflavor == gcfKlipper) - toggle_field("accel_to_decel_factor", config->opt_bool("accel_to_decel_enable")); + if((gcflavor == gcfKlipper).get_value()) + toggle_field("accel_to_decel_factor", ToggleExpr::FromConfigBool(config, "accel_to_decel_enable")); - bool have_make_overhang_printable = config->opt_bool("make_overhang_printable"); + auto have_make_overhang_printable = ToggleExpr::FromConfigBool(config, "make_overhang_printable"); toggle_line("make_overhang_printable_angle", have_make_overhang_printable); toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable); - toggle_line("min_width_top_surface", config->opt_bool("only_one_wall_top") || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value + toggle_line("min_width_top_surface", ToggleExpr::FromConfigBool(config, "only_one_wall_top") || (ToggleExpr::FromConfigFloat(config, "min_length_factor") > 0.5f) && have_arachne); // 0.5 is default value for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" }) - toggle_line(el, config->opt_bool("hole_to_polyhole")); + toggle_line(el, ToggleExpr::FromConfigBool(config, "hole_to_polyhole")); - bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall"); - bool has_overhang_reverse = config->opt_bool("overhang_reverse"); - bool force_wall_direction = config->opt_enum("wall_direction") != WallDirection::Auto; - bool allow_overhang_reverse = !has_spiral_vase && !force_wall_direction; + auto has_detect_overhang_wall = ToggleExpr::FromConfigBool(config, "detect_overhang_wall"); + auto has_overhang_reverse = ToggleExpr::FromConfigBool(config, "overhang_reverse"); + auto force_wall_direction = ToggleExpr::FromConfigEnum(config, "wall_direction") != WallDirection::Auto; + auto allow_overhang_reverse = !has_spiral_vase && !force_wall_direction; toggle_line("overhang_reverse", allow_overhang_reverse); toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse); - bool has_overhang_reverse_internal_only = config->opt_bool("overhang_reverse_internal_only"); - if (has_overhang_reverse_internal_only){ + auto has_overhang_reverse_internal_only = ToggleExpr::FromConfigBool(config, "overhang_reverse_internal_only"); + if (has_overhang_reverse_internal_only.get_value()){ DynamicPrintConfig new_conf = *config; new_conf.set_key_value("overhang_reverse_threshold", new ConfigOptionFloatOrPercent(0,true)); apply(config, &new_conf); } toggle_line("overhang_reverse_threshold", has_detect_overhang_wall && allow_overhang_reverse && has_overhang_reverse && !has_overhang_reverse_internal_only); - toggle_line("timelapse_type", is_BBL_Printer); + toggle_line("timelapse_type", is_BBL_printer); - bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation"); + auto have_small_area_infill_flow_compensation = ToggleExpr::FromConfigBool(config, "small_area_infill_flow_compensation"); toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation); toggle_field("seam_slope_type", !has_spiral_vase); - bool has_seam_slope = !has_spiral_vase && config->opt_enum("seam_slope_type") != SeamScarfType::None; + auto has_seam_slope = !has_spiral_vase && ToggleExpr::FromConfigEnum(config, "seam_slope_type") != SeamScarfType::None; toggle_line("seam_slope_conditional", has_seam_slope); toggle_line("seam_slope_start_height", has_seam_slope); toggle_line("seam_slope_entire_loop", has_seam_slope); @@ -929,11 +926,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("seam_slope_inner_walls", has_seam_slope); toggle_line("scarf_joint_speed", has_seam_slope); toggle_line("scarf_joint_flow_ratio", has_seam_slope); - toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop")); - toggle_line("scarf_angle_threshold", has_seam_slope && config->opt_bool("seam_slope_conditional")); - toggle_line("scarf_overhang_threshold", has_seam_slope && config->opt_bool("seam_slope_conditional")); + toggle_field("seam_slope_min_length", !ToggleExpr::FromConfigBool(config, "seam_slope_entire_loop")); + toggle_line("scarf_angle_threshold", has_seam_slope && ToggleExpr::FromConfigBool(config, "seam_slope_conditional")); + toggle_line("scarf_overhang_threshold", has_seam_slope && ToggleExpr::FromConfigBool(config, "seam_slope_conditional")); - bool use_beam_interlocking = config->opt_bool("interlocking_beam"); + auto use_beam_interlocking = ToggleExpr::FromConfigBool(config, "interlocking_beam"); toggle_line("mmu_segmented_region_interlocking_depth", !use_beam_interlocking); toggle_line("interlocking_beam_width", use_beam_interlocking); toggle_line("interlocking_orientation", use_beam_interlocking); @@ -941,22 +938,22 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("interlocking_depth", use_beam_interlocking); toggle_line("interlocking_boundary_avoidance", use_beam_interlocking); - bool lattice_options = config->opt_enum("sparse_infill_pattern") == InfillPattern::ipLateralLattice; + auto lattice_options = pattern == InfillPattern::ipLateralLattice; for (auto el : { "lateral_lattice_angle_1", "lateral_lattice_angle_2"}) toggle_line(el, lattice_options); - + // Adaptative Cubic and support cubic infill patterns do not support infill rotation. - bool FillAdaptive = (pattern == InfillPattern::ipAdaptiveCubic || pattern == InfillPattern::ipSupportCubic); + auto FillAdaptive = pattern == InfillPattern::ipAdaptiveCubic || pattern == InfillPattern::ipSupportCubic; //Orca: disable infill_direction/solid_infill_direction if sparse_infill_rotate_template/solid_infill_rotate_template is not empty value and adaptive cubic/support cubic infill pattern is not selected toggle_field("sparse_infill_rotate_template", !FillAdaptive); - toggle_field("infill_direction", config->opt_string("sparse_infill_rotate_template") == "" && !FillAdaptive); - toggle_field("solid_infill_direction", config->opt_string("solid_infill_rotate_template") == ""); - - toggle_line("infill_overhang_angle", config->opt_enum("sparse_infill_pattern") == InfillPattern::ipLateralHoneycomb); + toggle_field("infill_direction", ToggleExpr::FromConfigString(config, "sparse_infill_rotate_template") == "" && !FillAdaptive); + toggle_field("solid_infill_direction", ToggleExpr::FromConfigString(config, "solid_infill_rotate_template") == ""); + + toggle_line("infill_overhang_angle", pattern == InfillPattern::ipLateralHoneycomb); std::string printer_type = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle); - toggle_line("enable_wrapping_detection", DevPrinterConfigUtil::support_wrapping_detection(printer_type)); + toggle_line("enable_wrapping_detection", ToggleExpr(DevPrinterConfigUtil::support_wrapping_detection(printer_type), "Printer does not support wrapping detection").disable_postfix()); } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) @@ -995,7 +992,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) { - bool supports_en = config->opt_bool("supports_enable"); + auto supports_en = ToggleExpr::FromConfigBool(config, "supports_enable"); toggle_field("support_head_front_diameter", supports_en); toggle_field("support_head_penetration", supports_en); @@ -1014,7 +1011,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("support_points_density_relative", supports_en); toggle_field("support_points_minimal_distance", supports_en); - bool pad_en = config->opt_bool("pad_enable"); + auto pad_en = ToggleExpr::FromConfigBool(config, "pad_enable"); toggle_field("pad_wall_thickness", pad_en); toggle_field("pad_wall_height", pad_en); @@ -1025,7 +1022,7 @@ void ConfigManipulation::toggle_print_sla_options(DynamicPrintConfig* config) toggle_field("pad_around_object", pad_en); toggle_field("pad_around_object_everywhere", pad_en); - bool zero_elev = config->opt_bool("pad_around_object") && pad_en; + auto zero_elev = ToggleExpr::FromConfigBool(config, "pad_around_object") && pad_en; toggle_field("support_object_elevation", supports_en && !zero_elev); toggle_field("pad_object_gap", zero_elev); diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index 59d7779d33..ecb8fc1400 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -10,6 +10,7 @@ #include "libslic3r/PrintConfig.hpp" #include "Field.hpp" +#include "ToggleExpr.hpp" namespace Slic3r { @@ -27,8 +28,8 @@ class ConfigManipulation // function to loading of changed configuration std::function load_config = nullptr; - std::function cb_toggle_field = nullptr; - std::function cb_toggle_line = nullptr; + std::function cb_toggle_field = nullptr; + std::function cb_toggle_line = nullptr; // callback to propagation of changed value, if needed std::function cb_value_change = nullptr; //BBS: change local config to const DynamicPrintConfig @@ -40,8 +41,8 @@ class ConfigManipulation public: ConfigManipulation(std::function load_config, - std::function cb_toggle_field, - std::function cb_toggle_line, + std::function cb_toggle_field, + std::function cb_toggle_line, std::function cb_value_change, //BBS: change local config to DynamicPrintConfig const DynamicPrintConfig* local_config = nullptr, @@ -65,8 +66,8 @@ public: void apply(DynamicPrintConfig* config, DynamicPrintConfig* new_config); t_config_option_keys const &applying_keys() const; - void toggle_field(const std::string& field_key, const bool toggle, int opt_index = -1); - void toggle_line(const std::string& field_key, const bool toggle, int opt_index = -1); + void toggle_field(const std::string& field_key, const ToggleExpr& toggle_expr, int opt_index = -1); + void toggle_line(const std::string& field_key, const ToggleExpr& toggle_expr, int opt_index = -1); // FFF print void update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config = false, const bool is_plate_config = false); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 09ca8c64a8..6e34a5851f 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -379,9 +379,9 @@ void ObjectSettings::update_config_values(ModelConfig* config) field->toggle(toggle); }; #else - auto toggle_field = [this](const t_config_option_key & opt_key, bool toggle, int opt_index) + auto toggle_field = [this](const t_config_option_key & opt_key, const ToggleExpr& toggle_expr, int opt_index) { - m_tab_active->toggle_option(opt_key, toggle, opt_index); + m_tab_active->toggle_option(opt_key, toggle_expr, opt_index); }; #endif diff --git a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp index 11cd00cdc8..2d51229b71 100644 --- a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp @@ -284,16 +284,16 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_ m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); m_og_settings.push_back(optgroup); - auto toggle_field = [this, optgroup](const t_config_option_key & opt_key, bool toggle, int opt_index) + auto toggle_field = [this, optgroup](const t_config_option_key & opt_key, const ToggleExpr& toggle_expr, int opt_index) { Field* field = optgroup->get_fieldc(opt_key, opt_index);; if (field) - field->toggle(toggle); + field->toggle(toggle_expr.get_value()); }; - auto toggle_line = [this, optgroup](const t_config_option_key &opt_key, bool toggle, int opt_index) + auto toggle_line = [this, optgroup](const t_config_option_key &opt_key, const ToggleExpr& toggle_expr, int opt_index) { Line* line = optgroup->get_line(opt_key); - if (line) line->toggle_visible = toggle; + if (line) line->toggle_visible = toggle_expr.get_value(); }; ConfigManipulation config_manipulation(nullptr, toggle_field, toggle_line, nullptr, &m_current_config); @@ -381,7 +381,7 @@ void ObjectTableSettings::update_config_values(bool is_object, ModelObject* obje DynamicPrintConfig &main_config = m_current_config; - auto toggle_field = [this](const t_config_option_key & opt_key, bool toggle, int opt_index) + auto toggle_field = [this](const t_config_option_key & opt_key, const ToggleExpr& toggle_expr, int opt_index) { Field* field = nullptr; for (auto og : m_og_settings) { @@ -390,12 +390,12 @@ void ObjectTableSettings::update_config_values(bool is_object, ModelObject* obje break; } if (field) - field->toggle(toggle); + field->toggle(toggle_expr.get_value()); }; - auto toggle_line = [this](const t_config_option_key &opt_key, bool toggle, int opt_index) { + auto toggle_line = [this](const t_config_option_key &opt_key, const ToggleExpr& toggle_expr, int opt_index) { for (auto og : m_og_settings) { Line *line = og->get_line(opt_key); - if (line) { line->toggle_visible = toggle; break; } + if (line) { line->toggle_visible = toggle_expr.get_value(); break; } } }; From 6b0016baca624c6bca175bfff8e28b8fe491a23b Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 04:22:26 -0500 Subject: [PATCH 09/14] update ParamsPanel and remove old toggle_field and toggle_option functions --- src/slic3r/GUI/ParamsPanel.cpp | 9 ++------- src/slic3r/GUI/Tab.cpp | 16 ---------------- src/slic3r/GUI/Tab.hpp | 4 ---- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index a7f371a9a8..c8be985cb9 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -637,13 +637,8 @@ void ParamsPanel::set_active_tab(wxPanel* tab) } auto tab_print = dynamic_cast(m_tab_print); - if (cur_tab == m_tab_print) { - if (tab_print) - tab_print->toggle_line("print_flow_ratio", false); - } else { - if (tab_print) - tab_print->toggle_line("print_flow_ratio", false); - } + if (tab_print) + tab_print->toggle_line("print_flow_ratio", ToggleExpr(false, "Disabled")); } bool ParamsPanel::is_active_and_shown_tab(wxPanel* tab) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fb85702fad..00810eff67 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1413,15 +1413,6 @@ Field* Tab::get_field(const t_config_option_key& opt_key, Page** selected_page, return field; } -void Tab::toggle_option(const std::string& opt_key, bool toggle, int opt_index/* = -1*/) -{ - if (!m_active_page) - return; - Field* field = m_active_page->get_field(opt_key, opt_index); - if (field) - field->toggle(toggle); -} - void Tab::toggle_option(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index /*= -1*/) { if (!m_active_page) @@ -1436,13 +1427,6 @@ void Tab::toggle_option(const std::string& opt_key, const ToggleExpr& toggle_exp } } -void Tab::toggle_line(const std::string &opt_key, bool toggle, int opt_index) -{ - if (!m_active_page) return; - Line *line = m_active_page->get_line(opt_key, opt_index); - if (line) line->toggle_visible = toggle; -}; - void Tab::toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index /*= -1*/) { if (!m_active_page) return; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index eca3a497a5..c5182ebe30 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -390,12 +390,8 @@ public: Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); - [[deprecated]] - void toggle_option(const std::string &opt_key, bool toggle, int opt_index = -1); void toggle_option(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); - [[deprecated]] - void toggle_line(const std::string &opt_key, bool toggle, int opt_index = -1); // BBS: hide some line void toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); From e089ffe3be98610563197f848240c2a068ca73fd Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 04:28:31 -0500 Subject: [PATCH 10/14] Enable explicit bool operator --- src/slic3r/GUI/ConfigManipulation.cpp | 12 ++++++------ src/slic3r/GUI/Tab.cpp | 12 ++++++------ src/slic3r/GUI/ToggleExpr.hpp | 5 +++++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 52d4222f12..2d9d5d24bc 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -581,7 +581,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_field("enable_arc_fitting", !have_volumetric_extrusion_rate_slope); toggle_line("max_volumetric_extrusion_rate_slope_segment_length", have_volumetric_extrusion_rate_slope); toggle_line("extrusion_rate_smoothing_external_perimeter_only", have_volumetric_extrusion_rate_slope); - if(have_volumetric_extrusion_rate_slope.get_value()) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false)); + if(have_volumetric_extrusion_rate_slope) config->set_key_value("enable_arc_fitting", new ConfigOptionBool(false)); if(have_volumetric_extrusion_rate_slope_segment_length < 0.5) { DynamicPrintConfig new_conf = *config; new_conf.set_key_value("max_volumetric_extrusion_rate_slope_segment_length", new ConfigOptionFloat(1)); @@ -610,7 +610,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co pattern == ipLockedZag || pattern == ipMonotonic || pattern == ipMonotonicLine || pattern == ipZigZag || ToggleExpr(pattern_e == ipConcentricInternal || pattern_e == ipSupportBase, ""); // If there is infill, enable/disable fill_multiline according to whether the pattern supports multiline infill. - if (have_infill.get_value()) { + if (have_infill) { toggle_field("fill_multiline", !non_multiline_infill_pattern); // If the infill pattern does not support multiline fill_multiline is changed to 1. // Necessary when the pattern contains params.multiline (for example, triangles because they belong to the rectilinear class) @@ -678,7 +678,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_field(el, have_default_acceleration); auto machine_supports_junction_deviation = gcflavor == gcfMarlinFirmware; - if (machine_supports_junction_deviation.get_value()) { + if (machine_supports_junction_deviation) { if (const auto *machine_jd = preset_bundle->printers.get_edited_preset().config.option("machine_max_junction_deviation")) { machine_supports_junction_deviation = ToggleExpr(!machine_jd->values.empty(), "machine_max_junction_deviation is not set").disable_postfix() && ToggleExpr(machine_jd->values.front() > 0.0, "machine_max_junction_deviation[0]") > 0; } @@ -688,7 +688,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co ToggleExpr have_default_jerk = ToggleExpr(config->has("default_jerk") && config->opt_float("default_jerk") > 0, "default_jerk") > "0"; for (auto el : { "outer_wall_jerk", "inner_wall_jerk", "initial_layer_jerk", "top_surface_jerk", "travel_jerk", "infill_jerk"}) { toggle_line(el, !machine_supports_junction_deviation); - if (machine_supports_junction_deviation.get_value()) + if (machine_supports_junction_deviation) toggle_field(el, have_default_jerk); } @@ -884,7 +884,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : {"accel_to_decel_enable", "accel_to_decel_factor"}) toggle_line(el, gcflavor == gcfKlipper); - if((gcflavor == gcfKlipper).get_value()) + if(gcflavor == gcfKlipper) toggle_field("accel_to_decel_factor", ToggleExpr::FromConfigBool(config, "accel_to_decel_enable")); auto have_make_overhang_printable = ToggleExpr::FromConfigBool(config, "make_overhang_printable"); @@ -903,7 +903,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("overhang_reverse", allow_overhang_reverse); toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse); auto has_overhang_reverse_internal_only = ToggleExpr::FromConfigBool(config, "overhang_reverse_internal_only"); - if (has_overhang_reverse_internal_only.get_value()){ + if (has_overhang_reverse_internal_only){ DynamicPrintConfig new_conf = *config; new_conf.set_key_value("overhang_reverse_threshold", new ConfigOptionFloatOrPercent(0,true)); apply(config, &new_conf); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 00810eff67..c5352c9a10 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1420,7 +1420,7 @@ void Tab::toggle_option(const std::string& opt_key, const ToggleExpr& toggle_exp Field* field = m_active_page->get_field(opt_key, opt_index); if (field) { field->toggle(toggle_expr.get_value()); - if (toggle_expr.get_value()) + if (toggle_expr) field->disabled_reasons.clear(); else field->disabled_reasons = toggle_expr.get_reasons(); @@ -1433,7 +1433,7 @@ void Tab::toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, Line *line = m_active_page->get_line(opt_key, opt_index); if (line) { line->toggle_visible = toggle_expr.get_value(); - if (toggle_expr.get_value()) + if (toggle_expr) line->hidden_reasons.clear(); else line->hidden_reasons = toggle_expr.get_reasons(); @@ -4208,7 +4208,7 @@ void TabFilament::toggle_options() DynamicConfig& proj_cfg = m_preset_bundle->project_config; std::string bed_temp_1st_layer_key; auto has_bed_type= ToggleExpr(proj_cfg.has("curr_bed_type"), "curr_bed_type").set_postfixes(" not set", " is set"); - if (has_bed_type.get_value()) + if (has_bed_type) { bed_temp_1st_layer_key = get_bed_temp_1st_layer_key(proj_cfg.opt_enum("curr_bed_type")); } @@ -5262,7 +5262,7 @@ void TabPrinter::toggle_options() toggle_option(el, !is_BBL_printer && !is_QIDI_printer); auto bSEMM = ToggleExpr::FromConfigBool(m_config, "single_extruder_multi_material"); - if ((!bSEMM && ToggleExpr::FromConfigBool(m_config, "manual_filament_change")).get_value()) { + if (!bSEMM && ToggleExpr::FromConfigBool(m_config, "manual_filament_change")) { DynamicPrintConfig new_conf = *m_config; new_conf.set_key_value("manual_filament_change", new ConfigOptionBool(false)); load_config(new_conf); @@ -5321,7 +5321,7 @@ void TabPrinter::toggle_options() auto wipe = retraction && ToggleExpr::FromConfigBool(m_config, "wipe", variant_index); toggle_option("retract_before_wipe", wipe, i); - if ((use_firmware_retraction && wipe).get_value()) { + if (use_firmware_retraction && wipe) { //wxMessageDialog dialog(parent(), MessageDialog dialog(parent(), _(L("The Wipe option is not available when using the Firmware Retraction mode.\n" @@ -5369,7 +5369,7 @@ void TabPrinter::toggle_options() // Check if junction deviation value is non-zero and firmware is Marlin auto enable_jerk = !gcf_is_marlin; - if ((gcf == gcfMarlinFirmware).get_value()) { + if (gcf == gcfMarlinFirmware) { const auto *junction_deviation = m_config->option("machine_max_junction_deviation"); if (junction_deviation != nullptr) { const auto &values = junction_deviation->values; diff --git a/src/slic3r/GUI/ToggleExpr.hpp b/src/slic3r/GUI/ToggleExpr.hpp index fadc59332d..ca6ada5e83 100644 --- a/src/slic3r/GUI/ToggleExpr.hpp +++ b/src/slic3r/GUI/ToggleExpr.hpp @@ -41,6 +41,11 @@ public: } } + explicit operator bool() const + { + return get_value(); + } + friend ToggleExpr operator&&(const ToggleExpr& lhs, const ToggleExpr& rhs) { if (lhs.m_value && rhs.m_value) From c9849170462bdc8af16ba14a57291344ad3ddbc0 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 10:41:30 -0500 Subject: [PATCH 11/14] Various improvements - Move ToggleExpr parsing to node based - Move reasons from vectors to sets to prevent duplication - Clear all reasons at the beginning of Tab::toggle_options --- src/slic3r/GUI/Field.hpp | 2 +- src/slic3r/GUI/OptionsGroup.cpp | 8 ++ src/slic3r/GUI/OptionsGroup.hpp | 3 +- src/slic3r/GUI/Tab.cpp | 26 ++++-- src/slic3r/GUI/Tab.hpp | 2 +- src/slic3r/GUI/ToggleExpr.cpp | 87 +++++++++++++----- src/slic3r/GUI/ToggleExpr.hpp | 152 ++++++++++++++++++++------------ 7 files changed, 193 insertions(+), 87 deletions(-) diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 4b19db7c87..294c76548e 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -207,7 +207,7 @@ public: /// Callback function to edit field value t_back_to_init m_fn_edit_value{ nullptr }; - std::vector disabled_reasons{}; + std::set disabled_reasons{}; // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 5b4f9d3504..dd92e04496 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1300,6 +1300,14 @@ void ExtruderOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const OptionsGroup::on_change_OG(opt_id, value); } +void OptionsGroup::clear_disabled_reasons() +{ + for (auto& field : m_fields) + field.second->disabled_reasons.clear(); + for (auto& line : m_lines) + line.hidden_reasons.clear(); +} + wxString OptionsGroup::get_url(const std::string& path_end) { //BBS diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index f1d86c3e81..befc5e8c49 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -57,7 +57,7 @@ public: std::string label_path; bool undo_to_sys{false}; // BBS: object config bool toggle_visible{true}; // BBS: hide some line - std::vector hidden_reasons; // Why was the visibility toggled + std::set hidden_reasons; // Why was the visibility toggled size_t full_width {0}; widget_t widget {nullptr}; @@ -241,6 +241,7 @@ protected: virtual void back_to_sys_value(const std::string& opt_key) {} public: + void clear_disabled_reasons(); static wxString get_url(const std::string& path_end); static bool launch_browser(const std::string& path_end); }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c5352c9a10..474b612a19 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1419,11 +1419,12 @@ void Tab::toggle_option(const std::string& opt_key, const ToggleExpr& toggle_exp return; Field* field = m_active_page->get_field(opt_key, opt_index); if (field) { - field->toggle(toggle_expr.get_value()); - if (toggle_expr) + auto [value, reasons] = toggle_expr.get_result(); + field->toggle(value); + if (value) field->disabled_reasons.clear(); else - field->disabled_reasons = toggle_expr.get_reasons(); + field->disabled_reasons.merge(reasons); } } @@ -1432,14 +1433,22 @@ void Tab::toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, if (!m_active_page) return; Line *line = m_active_page->get_line(opt_key, opt_index); if (line) { - line->toggle_visible = toggle_expr.get_value(); - if (toggle_expr) + auto [value, reasons] = toggle_expr.get_result(); + line->toggle_visible = value; + if (value) line->hidden_reasons.clear(); else - line->hidden_reasons = toggle_expr.get_reasons(); + line->hidden_reasons.merge(reasons); } } +void Tab::clear_disabled_reasons() +{ + if (!m_active_page) return; + for (auto optgroup : m_active_page->m_optgroups) + optgroup->clear_disabled_reasons(); +} + // To be called by custom widgets, load a value into a config, // update the preset selection boxes (the dirty flags) // If value is saved before calling this function, put saved_value = true, @@ -2758,6 +2767,7 @@ void TabPrint::toggle_options() { if (!m_active_page) return; // BBS: whether the preset is Bambu Lab printer + this->clear_disabled_reasons(); if (m_preset_bundle) { bool is_BBL_printer = wxGetApp().preset_bundle->is_bbl_vendor(); m_config_manipulation.set_is_BBL_Printer(is_BBL_printer); @@ -4179,6 +4189,7 @@ void TabFilament::toggle_options() { if (!m_active_page) return; + this->clear_disabled_reasons(); bool b_is_BBL_printer = false; if (m_preset_bundle) { b_is_BBL_printer = @@ -5197,6 +5208,7 @@ void TabPrinter::toggle_options() { if (!m_active_page || m_presets->get_edited_preset().printer_technology() == ptSLA) return; + this->clear_disabled_reasons(); auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); auto extruders = m_config->option("extruder_type"); @@ -7424,6 +7436,7 @@ void TabSLAMaterial::reload_config() void TabSLAMaterial::toggle_options() { + this->clear_disabled_reasons(); const Preset ¤t_printer = m_preset_bundle->printers.get_edited_preset(); auto model = ToggleExpr::FromConfigString(¤t_printer.config, "printer_model"); m_config_manipulation.toggle_field("material_print_speed", model != "SL1"); @@ -7579,6 +7592,7 @@ void TabSLAPrint::update_description_lines() void TabSLAPrint::toggle_options() { + this->clear_disabled_reasons(); if (m_active_page) m_config_manipulation.toggle_print_sla_options(m_config); } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index c5182ebe30..ea9baa1a75 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -391,8 +391,8 @@ public: Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); void toggle_option(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); - void toggle_line(const std::string& opt_key, const ToggleExpr& toggle_expr, int opt_index = -1); + void clear_disabled_reasons(); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString); bool current_preset_is_dirty() const; diff --git a/src/slic3r/GUI/ToggleExpr.cpp b/src/slic3r/GUI/ToggleExpr.cpp index c2208ad91e..6bbfeb0b7a 100644 --- a/src/slic3r/GUI/ToggleExpr.cpp +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -3,45 +3,91 @@ namespace Slic3r { namespace GUI { -void ToggleExpr::get_reasons(std::vector& reasons, const ToggleExpr& expr) +std::string ToggleExpr::Leaf::get_reason(const bool inverted) const { - auto& expr_reasons = expr.m_reasons; - if (expr_reasons.empty()) - return; - if (expr.m_unformatted_reason) { - // If the reason is unformatted, there should only be one reason in the list, - // but there can be cases where there is a single formatted reason passed from a different ToggleExpr - assert(expr_reasons.size() == 1); - reasons.emplace_back(expr.get_prefix() + expr_reasons[0] + expr.get_postfix()); - return; - } - - reasons.insert(reasons.end(), expr_reasons.begin(), expr_reasons.end()); + return get_prefix(inverted) + m_name + get_postfix(inverted); } -std::string ToggleExpr::get_prefix() const + +std::string ToggleExpr::Leaf::get_prefix(const bool inverted) const { if (!m_has_prefixes) return ""; - std::string p = m_is_not ? m_inverted_prefix : m_standard_prefix; + std::string p = inverted ? m_inverted_prefix : m_standard_prefix; if (!p.empty() && p.back() != ' ') p += " "; return p; } -std::string ToggleExpr::get_postfix() const +std::string ToggleExpr::Leaf::get_postfix(const bool inverted) const { if (m_disable_postfix) return ""; if (!m_comparison_val.empty() && m_comp_type != CompareType::NO_CT) { - return " " + comparison_type_to_string(m_comp_type, !m_is_not) + " " + m_comparison_val; + return " " + comparison_type_to_string(m_comp_type, !inverted) + " " + m_comparison_val; } - std::string p = m_is_not ? m_inverted_postfix : m_standard_postfix; + std::string p = inverted ? m_inverted_postfix : m_standard_postfix; if (!p.empty() && p.front() != ' ') p.insert(p.begin(), ' '); return p; } -std::string ToggleExpr::comparison_type_to_string(CompareType type, bool inverted) +std::pair> ToggleExpr::Node::evaluate(const bool inverted) const +{ + struct EvalVisitor + { + bool inverted; + std::pair> operator()(const And& data_and) const + { + auto [lhs_val, lhs_reasons] = data_and.lhs->evaluate(inverted); + auto [rhs_val, rhs_reasons] = data_and.rhs->evaluate(inverted); + + bool result = lhs_val && rhs_val; + // Result was a success, ignore any reason strings + if (result) + return {result, {}}; + + std::set reasons; + if (!lhs_val) + reasons.merge(lhs_reasons); + if (!rhs_val) + reasons.merge(rhs_reasons); + return {result, std::move(reasons)}; + } + std::pair> operator()(const Or& data_or) const + { + auto [lhs_val, lhs_reasons] = data_or.lhs->evaluate(inverted); + auto [rhs_val, rhs_reasons] = data_or.rhs->evaluate(inverted); + + bool result = lhs_val || rhs_val; + + std::set reasons; + if (!lhs_val) { + reasons.merge(lhs_reasons); + } + if (!rhs_val) + reasons.merge(rhs_reasons); + return {result, std::move(reasons)}; + } + std::pair> operator()(const Not& data_not) const + { + return data_not.child->evaluate(!inverted); + } + std::pair> operator()(const Leaf& data_leaf) const + { + bool value = inverted ? !data_leaf.m_value : data_leaf.m_value; + + std::set reasons; + if (!value) + reasons = {data_leaf.get_reason(inverted)}; + + return {value, std::move(reasons)}; + } + }; + EvalVisitor obj{inverted}; + return std::visit(obj, data); +} + +std::string ToggleExpr::comparison_type_to_string(const CompareType type, const bool inverted) { if (!inverted) { switch (type) { @@ -65,6 +111,7 @@ std::string ToggleExpr::comparison_type_to_string(CompareType type, bool inverte } } } + ToggleExpr ToggleExpr::FromConfigBool(const DynamicPrintConfig* config, const std::string& opt_key, unsigned opt_idx) { auto val = opt_idx == -1 ? config->opt_bool(opt_key) : config->opt_bool(opt_key, opt_idx); @@ -107,7 +154,7 @@ ToggleExprFragment ToggleExpr::FromConfigString(const DynamicPrintC return {config, opt_key, opt_idx}; } -std::string ToggleExpr::build_reasons_string(std::string beginning_message, const std::vector& reasons) +std::string ToggleExpr::build_reasons_string(std::string beginning_message, const std::set& reasons) { if (reasons.empty()) return ""; auto message = std::move(beginning_message); diff --git a/src/slic3r/GUI/ToggleExpr.hpp b/src/slic3r/GUI/ToggleExpr.hpp index ca6ada5e83..4bc9f63ff3 100644 --- a/src/slic3r/GUI/ToggleExpr.hpp +++ b/src/slic3r/GUI/ToggleExpr.hpp @@ -10,37 +10,76 @@ template class ToggleExprFragment; class ToggleExpr { - bool m_value{}; - bool m_is_not{}; + struct Result + { + bool value; + std::set reasons; + }; - // If the value indicates toggle off, these are the reasons why - std::vector m_reasons{}; - bool m_unformatted_reason{false}; + struct Node; + using NodePtr = std::shared_ptr; - bool m_has_prefixes{}; - std::string m_standard_prefix{}; - std::string m_inverted_prefix{}; + struct And + { + NodePtr lhs, rhs; + }; + struct Or + { + NodePtr lhs, rhs; + }; + struct Not + { + NodePtr child; + }; + struct Leaf + { + bool m_value; + std::string m_name; - bool m_disable_postfix{}; - std::string m_standard_postfix{" disabled"}; - std::string m_inverted_postfix{" enabled"}; + bool m_has_prefixes{}; + std::string m_standard_prefix{}; + std::string m_inverted_prefix{}; - CompareType m_comp_type{CompareType::NO_CT}; - std::string m_comparison_val{}; + bool m_disable_postfix{}; + std::string m_standard_postfix{" disabled"}; + std::string m_inverted_postfix{" enabled"}; - ToggleExpr(bool value, std::vector reasons) : m_reasons(std::move(reasons)) {} + CompareType m_comp_type{CompareType::NO_CT}; + std::string m_comparison_val{}; - static void get_reasons(std::vector& reasons, const ToggleExpr& expr); + Leaf(const bool value, std::string name) : m_value(value), m_name(std::move(name)) {} + + [[nodiscard]] std::string get_reason(bool inverted) const; + private: + [[nodiscard]] std::string get_prefix(bool inverted) const; + [[nodiscard]] std::string get_postfix(bool inverted) const; + }; + + struct Node + { + using NodeData = std::variant; + NodeData data; + + explicit Node(NodeData _data) : data(std::move(_data)) {} + + std::pair> evaluate(bool inverted = false) const; + }; + + template + struct visitor : Ts... { using Ts::operator()...; }; + template + visitor(Ts...) -> visitor; + + NodePtr m_node; public: - ToggleExpr(bool value, std::string name) : m_value(value) + ToggleExpr(const bool value, std::string name) { - if (!name.empty()) { - m_unformatted_reason = true; - m_reasons = {std::move(name)}; - } + m_node = std::make_shared(Leaf{value, std::move(name)}); } + ToggleExpr(std::shared_ptr node) : m_node(std::move(node)) {} + explicit operator bool() const { return get_value(); @@ -48,35 +87,17 @@ public: friend ToggleExpr operator&&(const ToggleExpr& lhs, const ToggleExpr& rhs) { - if (lhs.m_value && rhs.m_value) - return {true, ""}; - - std::vector reasons; - if (!lhs.m_value) - get_reasons(reasons, lhs); - if (!rhs.m_value) - get_reasons(reasons, rhs); - - return {false, reasons}; + return ToggleExpr(std::make_shared(And{lhs.m_node, rhs.m_node})); } friend ToggleExpr operator||(const ToggleExpr& lhs, const ToggleExpr& rhs) { - if (lhs.m_value || rhs.m_value) - return {true, ""}; - - std::vector reasons; - get_reasons(reasons, lhs); - get_reasons(reasons, rhs); - return {false, reasons}; + return ToggleExpr(std::make_shared(Or{lhs.m_node, rhs.m_node})); } friend ToggleExpr operator!(const ToggleExpr& rhs) { - auto copy = rhs; - copy.m_value = !copy.m_value; - copy.m_is_not = !copy.m_is_not; - return copy; + return ToggleExpr(std::make_shared(Not{rhs.m_node})); } // For ToggleExpr objects that can be LHS @@ -95,31 +116,38 @@ public: friend ToggleExpr& operator>=(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::GTE, std::move(rhs)); } friend ToggleExpr& operator<=(ToggleExpr lhs, std::string rhs) { return lhs.set_comparison(CompareType::LTE, std::move(rhs)); } - [[nodiscard]] std::string get_prefix() const; - - [[nodiscard]] std::string get_postfix() const; - /// /// \param value_false_prefix When the provided value is false, what should the prefix be? /// \param opposite_prefix When the provided value is false when inverted ('!' operator), what should the prefix be? /// \return self ToggleExpr& set_prefixes(std::string value_false_prefix, std::string opposite_prefix) { - m_standard_prefix = std::move(value_false_prefix); - m_inverted_prefix = std::move(opposite_prefix); + std::visit(visitor{[](auto& data) { static_assert("Calling set_prefixes on a non leaf node"); }, + [&](Leaf& leaf) { + leaf.m_standard_prefix = std::move(value_false_prefix); + leaf.m_inverted_prefix = std::move(opposite_prefix); + }}, + m_node->data); return *this; } ToggleExpr& disable_postfix() { - m_disable_postfix = true; + std::visit(visitor{[](auto&) { static_assert("Calling disable_postfix on a non leaf node"); }, + [&](Leaf& leaf) { leaf.m_disable_postfix = true; }}, + m_node->data); return *this; } ToggleExpr& set_postfixes(std::string value_false_postfix, std::string opposite_postfix) { - m_standard_postfix = std::move(value_false_postfix); - m_inverted_postfix = std::move(opposite_postfix); + std::visit(visitor{[](auto&) { static_assert("Calling set_postfixes on a non leaf node"); }, + [&](Leaf& leaf) { + leaf.m_standard_postfix = std::move(value_false_postfix); + leaf.m_inverted_postfix = std::move(opposite_postfix); + }}, + m_node->data); + return *this; } @@ -127,18 +155,26 @@ public: /// \return self ToggleExpr& set_comparison(CompareType comp_type, std::string comparison_val) { - m_comp_type = comp_type; - m_comparison_val = std::move(comparison_val); + std::visit(visitor{[](auto& data) { static_assert("Calling set_comparison on a non leaf node"); }, + [&](Leaf& leaf) { + leaf.m_comp_type = comp_type; + leaf.m_comparison_val = std::move(comparison_val); + }}, + m_node->data); + return *this; } - [[nodiscard]] bool get_value() const { return m_value; } + [[nodiscard]] bool get_value() const { return m_node->evaluate().first; } - [[nodiscard]] std::vector get_reasons() const + [[nodiscard]] std::set get_reasons() const { - std::vector out; - get_reasons(out, *this); - return out; + return m_node->evaluate().second; + } + + [[nodiscard]] std::pair> get_result() const + { + return m_node->evaluate(); } template static bool compare(T value, CompareType comp_type, T comp_value) @@ -197,7 +233,7 @@ public: const std::string& opt_key, unsigned opt_idx = -1); - static std::string build_reasons_string(std::string beginning_message, const std::vector& reasons); + static std::string build_reasons_string(std::string beginning_message, const std::set& reasons); }; template class ToggleExprFragment From 354ad2f2383b158ba85056269a8e0e1699acb3ce Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 10:45:23 -0500 Subject: [PATCH 12/14] Cleanup --- src/slic3r/GUI/Field.cpp | 3 --- src/slic3r/GUI/Field.hpp | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index f76d35e7d1..2cc3aa478e 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -23,7 +23,6 @@ // BBS #include "Notebook.hpp" -#include "ToggleExpr.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/TextInput.hpp" #include "Widgets/SpinInput.hpp" @@ -31,8 +30,6 @@ #include "Widgets/TextCtrl.h" #include "../Utils/ColorSpaceConvert.hpp" - -#include #ifdef __WXOSX__ #define wxOSX true #else diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 294c76548e..16d346c826 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -5,7 +5,6 @@ #ifndef WX_PRECOMP #include #endif -#include #include #include @@ -291,11 +290,6 @@ protected: wxString m_na_value = _(L("N/A")); - // When the field is disabled, so are tooltips. - // Tooltips are manually created while the window is disabled - wxTipWindow* m_tooltip{nullptr}; - wxTimer m_tooltip_timer; - friend class OptionsGroup; }; From f15643d209384ca0844a25f622d597c53c9d0549 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Fri, 20 Feb 2026 14:01:54 -0500 Subject: [PATCH 13/14] Fix compilation error --- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 424ef51831..9b626a471b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -817,7 +817,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line(el, have_prime_tower); toggle_line("enable_tower_interface_cooldown_during_tower", - have_prime_tower && config->opt_bool("enable_tower_interface_features")); + have_prime_tower && ToggleExpr::FromConfigBool(config, "enable_tower_interface_features")); for (auto el : {"wall_filament", "sparse_infill_filament", "solid_infill_filament", "wipe_tower_filament"}) toggle_line(el, !bSEMM); From 8a62cf28e8c6f4eff25234f9b7a767c0f353891e Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Sat, 21 Feb 2026 09:41:02 -0500 Subject: [PATCH 14/14] Move to platform agnostic impl --- src/slic3r/GUI/OG_CustomCtrl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 9f8817f198..953cc1b2ff 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -407,8 +407,9 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event) if (!field) continue; auto win = field->getWindow(); if (!win) continue; + auto tooltips = win->GetToolTip(); - auto tip = win->HasToolTips() ? win->GetToolTipText() : ""; + auto tip = tooltips ? tooltips->GetTip() : ""; tip += "\n\n"; tip += ToggleExpr::build_reasons_string("Option is disabled.", field->disabled_reasons); tip.Trim(true);