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 4c3bf26737..63f8906582 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/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 2ff580717f..9b626a471b 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,10 +572,11 @@ 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); @@ -587,66 +588,67 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co 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); + 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) { 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) 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,137 +789,138 @@ 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", "enable_tower_interface_features"}) 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); - 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")); + 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"); + auto has_overhang_reverse_internal_only = ToggleExpr::FromConfigBool(config, "overhang_reverse_internal_only"); 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); } 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); @@ -932,11 +929,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); @@ -944,22 +941,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*/) @@ -998,7 +995,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); @@ -1017,7 +1014,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); @@ -1028,7 +1025,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/Field.hpp b/src/slic3r/GUI/Field.hpp index a668ea3fab..e197aa1636 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -207,6 +207,8 @@ public: /// Callback function to edit field value t_back_to_init m_fn_edit_value{ nullptr }; + std::set 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}; @@ -288,7 +290,7 @@ protected: bool bEnterPressed = false; wxString m_na_value = _(L("N/A")); - + friend class OptionsGroup; }; 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; } } }; diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 331d4a5db8..953cc1b2ff 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,27 @@ 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 tooltips = win->GetToolTip(); + + auto tip = tooltips ? tooltips->GetTip() : ""; + 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 diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 534646d593..8749ade3f4 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1301,6 +1301,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 ce523cbedc..befc5e8c49 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::set hidden_reasons; // Why was the visibility toggled size_t full_width {0}; widget_t widget {nullptr}; @@ -240,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/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 469412a74c..aea32dca9a 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 { @@ -1429,21 +1431,41 @@ 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*/) +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); + if (field) { + auto [value, reasons] = toggle_expr.get_result(); + field->toggle(value); + if (value) + field->disabled_reasons.clear(); + else + field->disabled_reasons.merge(reasons); + } } -void Tab::toggle_line(const std::string &opt_key, bool toggle, int opt_index) +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; -}; + if (line) { + auto [value, reasons] = toggle_expr.get_result(); + line->toggle_visible = value; + if (value) + line->hidden_reasons.clear(); + else + 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) @@ -2027,6 +2049,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, @@ -2755,6 +2787,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); @@ -3755,16 +3788,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_")); @@ -4177,34 +4214,37 @@ void TabFilament::toggle_options() { if (!m_active_page) return; - bool is_BBL_printer = false; + this->clear_disabled_reasons(); + 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) { bed_temp_1st_layer_key = get_bed_temp_1st_layer_key(proj_cfg.opt_enum("curr_bed_type")); } @@ -4213,13 +4253,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, "")); } @@ -4229,20 +4272,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 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); } @@ -4256,13 +4299,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); } } @@ -5189,6 +5233,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"); @@ -5199,21 +5244,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 = 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 @@ -5224,15 +5276,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")) { @@ -5247,8 +5298,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")) { DynamicPrintConfig new_conf = *m_config; new_conf.set_key_value("manual_filament_change", new ConfigOptionBool(false)); load_config(new_conf); @@ -5265,15 +5316,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", variant_index) > 0; - 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") > "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::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 @@ -5282,7 +5335,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); @@ -5291,7 +5344,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", i) > 0, i); // some options only apply when not using firmware retraction vec.resize(0); @@ -5302,7 +5355,7 @@ 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) { @@ -5327,37 +5380,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", variant_index) > 0; + 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") != 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::FromConfigEnum(m_config, "z_hop_types", i) != ZHopType::zhtNormal, + 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") == "0"; } else { - enable_jerk = true; + enable_jerk = ToggleExpr(true, ""); } } for (int i = 0; i < max_field; ++i) { @@ -5367,7 +5422,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); } @@ -7408,8 +7463,9 @@ void TabSLAMaterial::reload_config() void TabSLAMaterial::toggle_options() { + this->clear_disabled_reasons(); 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"); } @@ -7563,6 +7619,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); } @@ -7612,12 +7669,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) { diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 96b35b26e1..ea9baa1a75 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,12 @@ 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); - void toggle_option(const std::string &opt_key, bool toggle, int opt_index = -1); - 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_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; 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..6bbfeb0b7a --- /dev/null +++ b/src/slic3r/GUI/ToggleExpr.cpp @@ -0,0 +1,174 @@ + +#include "ToggleExpr.hpp" + +namespace Slic3r { namespace GUI { + +std::string ToggleExpr::Leaf::get_reason(const bool inverted) const +{ + return get_prefix(inverted) + m_name + get_postfix(inverted); +} + +std::string ToggleExpr::Leaf::get_prefix(const bool inverted) const +{ + if (!m_has_prefixes) + return ""; + std::string p = inverted ? m_inverted_prefix : m_standard_prefix; + if (!p.empty() && p.back() != ' ') + p += " "; + return p; +} + +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, !inverted) + " " + m_comparison_val; + } + std::string p = inverted ? m_inverted_postfix : m_standard_postfix; + if (!p.empty() && p.front() != ' ') + p.insert(p.begin(), ' '); + return p; +} + +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) { + 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)); +} + +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) +{ + 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)); +} + +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::set& reasons) +{ + if (reasons.empty()) return ""; + 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 diff --git a/src/slic3r/GUI/ToggleExpr.hpp b/src/slic3r/GUI/ToggleExpr.hpp new file mode 100644 index 0000000000..4bc9f63ff3 --- /dev/null +++ b/src/slic3r/GUI/ToggleExpr.hpp @@ -0,0 +1,278 @@ + +#ifndef ORCASLICER_TOGGLEEXPR_HPP +#define ORCASLICER_TOGGLEEXPR_HPP + +namespace Slic3r::GUI { + +enum class CompareType { NO_CT, GT, LT, GTE, LTE, EQ, NEQ }; + +template class ToggleExprFragment; + +class ToggleExpr +{ + struct Result + { + bool value; + std::set reasons; + }; + + struct Node; + using NodePtr = std::shared_ptr; + + 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_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{}; + + 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(const bool value, std::string 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(); + } + + friend ToggleExpr operator&&(const ToggleExpr& lhs, const ToggleExpr& rhs) + { + return ToggleExpr(std::make_shared(And{lhs.m_node, rhs.m_node})); + } + + friend ToggleExpr operator||(const ToggleExpr& lhs, const ToggleExpr& rhs) + { + return ToggleExpr(std::make_shared(Or{lhs.m_node, rhs.m_node})); + } + + friend ToggleExpr operator!(const ToggleExpr& rhs) + { + return ToggleExpr(std::make_shared(Not{rhs.m_node})); + } + + // 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)); } + + /// + /// \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) + { + 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() + { + 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) + { + 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; + } + + /// Set the comparison that is occurring to generate the provided value + /// \return self + ToggleExpr& set_comparison(CompareType comp_type, std::string 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_node->evaluate().first; } + + [[nodiscard]] std::set get_reasons() const + { + 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) + { + 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); + + 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 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::set& 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