From eb30ea1eb8d510175bfd3d97e489e5de458732a8 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Fri, 2 Oct 2026 17:34:47 -0500 Subject: [PATCH 1/3] perf: speed up G-code export by 3-9% via cached config lookups (#16028) --- src/libslic3r/GCode.cpp | 58 +++++++++-------- src/libslic3r/GCode.hpp | 20 ++++++ src/libslic3r/Print.cpp | 12 ++-- src/libslic3r/Print.hpp | 11 +++- src/libslic3r/PrintApply.cpp | 2 +- tests/fff_print/test_gcodewriter.cpp | 62 +++++++++++++++++++ .../test_toolordering_nozzle_group.cpp | 16 +++++ 7 files changed, 148 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4c02cae7b4..25addff79d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4334,9 +4334,9 @@ size_t GCode::get_extruder_id(unsigned int filament_id) const size_t GCode::get_filament_config_index(int filament_id) const { - if (m_print) { - return m_print->get_filament_config_indx(filament_id, m_cur_layer_idx); - } + if (m_print) + return m_filament_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(), + [&] { return m_print->get_filament_config_indx(filament_id, m_cur_layer_idx); }); // Orca: without a Print the filament-indexed arrays are unexpanded, so the // filament id itself is the only meaningful column. return filament_id; @@ -4350,9 +4350,9 @@ size_t GCode::get_filament_config_index(int filament_id, size_t layer_id) const size_t GCode::get_nozzle_config_index(int filament_id) const { - if (m_print) { - return m_print->get_nozzle_config_index(filament_id, m_cur_layer_idx); - } + if (m_print) + return m_nozzle_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(), + [&] { return m_print->get_nozzle_config_index(filament_id, m_cur_layer_idx); }); // Orca: same reasoning; degenerate to the filament's extruder column. return get_extruder_id(filament_id); } @@ -8059,28 +8059,36 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, unsigned int acceleration_i = 0; double jerk = 0; // adjust acceleration - if (NOZZLE_CONFIG(default_acceleration) > 0) { + const size_t nozzle = get_nozzle_config_index(m_writer.filament()->id()); + if (m_config.default_acceleration.get_at(nozzle) > 0) { + const ExtrusionRole role = path.role(); + const double bridge_acceleration = is_bridge(role) ? + m_config.bridge_acceleration.get_at(nozzle).get_abs_value(m_config.outer_wall_acceleration.get_at(nozzle)) : 0.; + const double sparse_infill_acceleration = role == erInternalInfill ? + m_config.sparse_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.; + const double internal_solid_infill_acceleration = role == erSolidInfill ? + m_config.internal_solid_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.; double acceleration; - if (this->on_first_layer() && NOZZLE_CONFIG(initial_layer_acceleration) > 0) { - acceleration = NOZZLE_CONFIG(initial_layer_acceleration); + if (this->on_first_layer() && m_config.initial_layer_acceleration.get_at(nozzle) > 0) { + acceleration = m_config.initial_layer_acceleration.get_at(nozzle); #if 0 } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) { acceleration = m_config.first_layer_acceleration_over_raft.value; #endif - } else if (m_config.get_abs_value_at("bridge_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && is_bridge(path.role())) { - acceleration = m_config.get_abs_value_at("bridge_acceleration", get_nozzle_config_index(m_writer.filament()->id())); - } else if (m_config.get_abs_value_at("sparse_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && (path.role() == erInternalInfill)) { - acceleration = m_config.get_abs_value_at("sparse_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())); - } else if (m_config.get_abs_value_at("internal_solid_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())) > 0 && (path.role() == erSolidInfill)) { - acceleration = m_config.get_abs_value_at("internal_solid_infill_acceleration", get_nozzle_config_index(m_writer.filament()->id())); - } else if (NOZZLE_CONFIG(outer_wall_acceleration) > 0 && is_external_perimeter(path.role())) { - acceleration = NOZZLE_CONFIG(outer_wall_acceleration); - } else if (NOZZLE_CONFIG(inner_wall_acceleration) > 0 && is_internal_perimeter(path.role())) { - acceleration = NOZZLE_CONFIG(inner_wall_acceleration); - } else if (NOZZLE_CONFIG(top_surface_acceleration) > 0 && is_top_surface(path.role())) { - acceleration = NOZZLE_CONFIG(top_surface_acceleration); + } else if (bridge_acceleration > 0) { + acceleration = bridge_acceleration; + } else if (sparse_infill_acceleration > 0) { + acceleration = sparse_infill_acceleration; + } else if (internal_solid_infill_acceleration > 0) { + acceleration = internal_solid_infill_acceleration; + } else if (m_config.outer_wall_acceleration.get_at(nozzle) > 0 && is_external_perimeter(role)) { + acceleration = m_config.outer_wall_acceleration.get_at(nozzle); + } else if (m_config.inner_wall_acceleration.get_at(nozzle) > 0 && is_internal_perimeter(role)) { + acceleration = m_config.inner_wall_acceleration.get_at(nozzle); + } else if (m_config.top_surface_acceleration.get_at(nozzle) > 0 && is_top_surface(role)) { + acceleration = m_config.top_surface_acceleration.get_at(nozzle); } else { - acceleration = NOZZLE_CONFIG(default_acceleration); + acceleration = m_config.default_acceleration.get_at(nozzle); } acceleration_i = (unsigned int)floor(acceleration + 0.5); } @@ -8181,7 +8189,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed)); } } else if(path.role() == erInternalBridgeInfill) { - speed = m_config.get_abs_value_at("internal_bridge_speed", get_nozzle_config_index(m_writer.filament()->id())); + speed = m_config.internal_bridge_speed.get_at(nozzle).get_abs_value(m_config.bridge_speed.get_at(nozzle)); } else if (path.role() == erOverhangPerimeter || path.role() == erSupportTransition || path.role() == erBridgeInfill) { speed = NOZZLE_CONFIG(bridge_speed); } else if (path.role() == erInternalInfill) { @@ -8193,7 +8201,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } else if (path.role() == erIroning) { const size_t filament_idx = get_filament_config_index(m_writer.filament()->id()); speed = m_config.filament_ironing_speed.is_nil(filament_idx) - ? m_config.get_abs_value("ironing_speed") + ? m_config.ironing_speed.value : m_config.filament_ironing_speed.get_at(filament_idx); } else if (path.role() == erBottomSurface) { speed = NOZZLE_CONFIG(initial_layer_infill_speed); @@ -8254,7 +8262,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // Override skirt speed if set if (path.role() == erSkirt) { - const double skirt_speed = m_config.get_abs_value("skirt_speed"); + const double skirt_speed = m_config.skirt_speed.value; if (skirt_speed > 0.0) speed = skirt_speed; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 29a8ae5a34..7567bc6e0d 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -301,6 +301,24 @@ public: size_t get_filament_config_index(int filament_id, size_t layer_id) const; size_t get_nozzle_config_index(int filament_id) const; + // Holds the last slot a resolver returned without locking, so only the G-code generator may + // call the resolvers. + struct ConfigIndexCache + { + bool valid{false}; + int filament_id{0}; + size_t layer_idx{0}; + size_t generation{0}; + size_t index{0}; + + template size_t get(int filament, size_t layer, size_t gen, Lookup &&lookup) + { + if (!valid || filament_id != filament || layer_idx != layer || generation != gen) + *this = {true, filament, layer, gen, size_t(lookup())}; + return index; + } + }; + // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp struct LayerToPrint @@ -784,6 +802,8 @@ private: // Object layer id of the layer being generated; keys the per-filament config-slot // resolvers. Distinct from m_layer_index (an export progress counter starting at -1). size_t m_cur_layer_idx{0}; + mutable ConfigIndexCache m_filament_index_cache; + mutable ConfigIndexCache m_nozzle_index_cache; std::set m_initial_layer_extruders; std::vector> m_sorted_layer_filaments; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6d60874c18..c3d157e1e6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -4298,10 +4298,11 @@ Polygons Print::get_extruder_shared_printable_polygon() const return shared_printable_polys; } -// Narrow the stored grouping result to the layer-aware type the slicing pipeline uses. -std::shared_ptr Print::get_layered_nozzle_group_result() const +void Print::set_nozzle_group_result(std::shared_ptr result) { - return std::dynamic_pointer_cast(m_nozzle_group_result); + m_nozzle_group_result = std::move(result); + m_layered_nozzle_group_result = std::dynamic_pointer_cast(m_nozzle_group_result); + ++m_config_index_generation; } // Dynamic (per-layer selector) regroup predicate. @@ -4337,6 +4338,7 @@ int Print::get_filament_config_indx(int filament_id, int layer_id, bool use_cach void Print::update_filament_self_index_cache() { m_missing_nozzle_group_logged.clear(); // reset the per-slice get_config_index log dedupe + ++m_config_index_generation; std::vector values; if (m_full_print_config.has("filament_self_index")) { @@ -4376,7 +4378,7 @@ int Print::get_nozzle_config_index(int filament_id, int layer_id) int Print::get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, FilamentIndexMap *index_map) { - auto group_result = get_layered_nozzle_group_result(); + const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get(); // Orca: defensive — when no grouping producer has published a result yet, fall back to the // static identity: one filament-variant column per filament. if (!group_result) @@ -4411,7 +4413,7 @@ int Print::get_config_index(int filament_id, int layer_id, const std::vector &variant_list, const std::vector& self_index_list, PrintIndexMap &index_map) { - auto group_result = get_layered_nozzle_group_result(); + const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get(); // Orca: same static fallback as the filament overload; the slot degenerates to the filament's // extruder column (filament_map is 1 based, get_extruder_id guards the filament id range). if (!group_result) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index f185962b1e..13562b4355 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -1073,9 +1073,9 @@ public: // Logical (extruder, nozzle) grouping result produced by ToolOrdering during reorder. // Consumed by GCode via get_layered_nozzle_group_result()->get_nozzle_id(filament, layer) etc. - void set_nozzle_group_result(std::shared_ptr result) { m_nozzle_group_result = result; } + void set_nozzle_group_result(std::shared_ptr result); std::shared_ptr get_nozzle_group_result() const { return m_nozzle_group_result; } - std::shared_ptr get_layered_nozzle_group_result() const; + std::shared_ptr get_layered_nozzle_group_result() const { return m_layered_nozzle_group_result; } // True only when the project opts into the per-layer filament selector // (enable_filament_dynamic_map) in auto-for-flush mode on a multi-extruder machine. Gates the @@ -1226,6 +1226,9 @@ public: // pipeline's cooling stage, which runs concurrently with the generator stage filling it. int get_filament_config_indx(int filament_id, int layer_id, bool use_cache = true); int get_nozzle_config_index(int filament_id, int layer_id); + // Changes with the grouping result and the filament maps, so a caller may reuse a resolved slot + // until it changes. + size_t config_index_generation() const { return m_config_index_generation; } // Orca: Implement prusa's filament shrink compensation approach // Returns if all used filaments have same shrinkage compensations. @@ -1352,6 +1355,9 @@ private: // Logical (extruder, nozzle) grouping result, set by ToolOrdering during reorder. std::shared_ptr m_nozzle_group_result; + // m_nozzle_group_result narrowed to the layer-aware type; only set_nozzle_group_result() assigns + // either. + std::shared_ptr m_layered_nozzle_group_result; // Sequential (by-object) selector plans, keyed by object; see sequential_dynamic_orderings(). // Rebuilt (or cleared) on every process(). @@ -1361,6 +1367,7 @@ private: FilamentIndexMap m_filament_index_map; // Used to cache printer and process parameter information PrintIndexMap m_nozzle_index_map; + size_t m_config_index_generation{0}; // Orca: filament ids already reported as missing a nozzle-group entry this slice. get_config_index() // falls back per-filament/per-layer in the g-code hot path, so this dedupes its log to once per // filament instead of flooding thousands of identical error lines. Cleared with the caches each slice. diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 21e2251b65..9b794d2787 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1309,7 +1309,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Reproduce that exact expansion here so an unchanged config diffs empty — the expanded // keys invalidate the wipe tower / g-code export, and the placeholder parser aliases // the full config — instead of trimming back to one slot per filament. - auto group_result = std::dynamic_pointer_cast(this->get_nozzle_group_result()); + auto group_result = this->get_layered_nozzle_group_result(); std::unordered_map> filament_variant_uses; if (group_result && group_result->is_support_dynamic_nozzle_map() && collect_filament_variant_uses(*group_result, m_ori_full_print_config, filament_variant_uses)) diff --git a/tests/fff_print/test_gcodewriter.cpp b/tests/fff_print/test_gcodewriter.cpp index f371fcb7f4..d3a0722fc1 100644 --- a/tests/fff_print/test_gcodewriter.cpp +++ b/tests/fff_print/test_gcodewriter.cpp @@ -4,18 +4,21 @@ #include #include #include +#include #include #include #include #include "nlohmann/json.hpp" +#include "libslic3r/GCodeReader.hpp" #include "libslic3r/GCodeWriter.hpp" #include "libslic3r/GCode.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/ModelArrange.hpp" +#include #include #include "test_helpers.hpp" @@ -97,6 +100,22 @@ SCENARIO("Origin manipulation", "[GCodeWriter]") { } } +TEST_CASE("A cached config slot is looked up again whenever its key changes", "[GCodeWriter]") +{ + GCode::ConfigIndexCache cache; + int lookups = 0; + auto slot = [&](int filament, size_t layer, size_t generation) { + return cache.get(filament, layer, generation, [&] { ++lookups; return filament * 100 + int(layer) * 10 + int(generation); }); + }; + REQUIRE(slot(1, 2, 3) == 123); + REQUIRE(slot(1, 2, 3) == 123); + REQUIRE(lookups == 1); + REQUIRE(slot(4, 2, 3) == 423); + REQUIRE(slot(4, 5, 3) == 453); + REQUIRE(slot(4, 5, 6) == 456); + REQUIRE(lookups == 4); +} + // Verify that emit_machine_limits_to_gcode emits the correct max value across // used extruders (regression for commit b4ee665: "Emit max value of machine // limit among used extruders"). @@ -872,3 +891,46 @@ TEST_CASE("Custom G-code motion limits are restored before generated moves", "[G REQUIRE(gcode.find("M204 S6000 ; adjust acceleration", custom_gcode_pos) != std::string::npos); REQUIRE(gcode.find("M205 X8 Y8 ; adjust jerk", custom_gcode_pos) != std::string::npos); } + +TEST_CASE("Percent accelerations resolve against the option they are a percentage of", "[GCodeWriter]") +{ + DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({ + { "gcode_flavor", "marlin" }, + { "machine_max_acceleration_extruding", "20000,20000" }, + { "default_acceleration", "4000" }, + { "initial_layer_acceleration", "0" }, + { "outer_wall_acceleration", "3000" }, + { "bridge_acceleration", "50%" }, + { "sparse_infill_acceleration", "25%" }, + { "internal_solid_infill_acceleration", "60%" }, + { "sparse_infill_density", "20%" }, + }); + // get_abs_value_at() resolves each percentage through the ratio_over in the config definitions. + const std::map expected = { + { "Bridge", int(config.get_abs_value_at("bridge_acceleration", 0)) }, + { "Sparse infill", int(config.get_abs_value_at("sparse_infill_acceleration", 0)) }, + { "Internal solid infill", int(config.get_abs_value_at("internal_solid_infill_acceleration", 0)) }, + }; + REQUIRE(expected.at("Bridge") == 1500); + REQUIRE(expected.at("Sparse infill") == 1000); + REQUIRE(expected.at("Internal solid infill") == 2400); + + std::map> accelerations_by_role; + std::string role; + int acceleration = 0; + GCodeReader reader; + reader.parse_buffer(Slic3r::Test::slice({ TestMesh::bridge }, config), [&](GCodeReader &self, const GCodeReader::GCodeLine &line) { + float value; + if (boost::starts_with(line.raw(), ";TYPE:")) + role = line.raw().substr(6); + else if (line.cmd_is("M204") && line.has_value('S', value)) + acceleration = int(value); + else if (line.extruding(self) && line.dist_XY(self) > 0) + accelerations_by_role[role].insert(acceleration); + }); + for (const auto &[role_name, value] : expected) { + INFO(role_name); + REQUIRE(accelerations_by_role[role_name] == std::set{ value }); + } +} diff --git a/tests/libslic3r/test_toolordering_nozzle_group.cpp b/tests/libslic3r/test_toolordering_nozzle_group.cpp index 4074a80995..837b66287a 100644 --- a/tests/libslic3r/test_toolordering_nozzle_group.cpp +++ b/tests/libslic3r/test_toolordering_nozzle_group.cpp @@ -510,6 +510,22 @@ TEST_CASE("Print config-index resolvers pick per-filament Hybrid slots", "[Print } } +TEST_CASE("Regrouping or rewriting the filament maps changes the config-index generation", "[Print][H2C]") +{ + Model model; + model.add_object("cube", "", make_cube(20, 20, 20))->add_instance(); + Print print; + print.apply(model, DynamicPrintConfig::full_print_config()); + + size_t generation = print.config_index_generation(); + print.set_nozzle_group_result(nullptr); + REQUIRE(print.config_index_generation() != generation); + + generation = print.config_index_generation(); + print.update_filament_maps_to_config({1}, {(int) nvtStandard}, {0}); + REQUIRE(print.config_index_generation() != generation); +} + TEST_CASE("Re-applying an unchanged config after slicing keeps the result valid", "[Print][H2C]") { // apply() rebuilds m_config.filament_map_2 to the real per-filament slot map, while the From 1241dd552189d90b12e749cc4b90fc623940ab05 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sat, 3 Oct 2026 03:02:36 +0300 Subject: [PATCH 2/3] Fix per-plate bed type handling (#15916) --- src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 2 + src/slic3r/GUI/ParamsDialog.cpp | 23 ++++++++++- src/slic3r/GUI/ParamsDialog.hpp | 1 + src/slic3r/GUI/PlateSettingsDialog.cpp | 5 ++- src/slic3r/GUI/Plater.cpp | 54 ++++++++++++++++++++++++-- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/Tab.cpp | 15 +++++++ src/slic3r/GUI/WebGuideDialog.cpp | 3 ++ 9 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6bd0797ac9..94ef20e9fd 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -6735,8 +6735,10 @@ void GUI_App::reload_settings() tab->reload_config(); tab->update_changed_ui(); } - if (plater_) + if (plater_) { plater_->sidebar().update_all_preset_comboboxes(); + plater_->normalize_bed_types(false); + } }; if (is_main_thread_active()) refresh_synced_ui(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 53507edfb3..07d51f9f0c 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -3870,6 +3870,8 @@ bool MainFrame::load_config_file(const std::string &path) return false; } wxGetApp().load_current_presets(); + if (Plater *plater = wxGetApp().plater()) + plater->normalize_bed_types(false); return true; } diff --git a/src/slic3r/GUI/ParamsDialog.cpp b/src/slic3r/GUI/ParamsDialog.cpp index 87888848a6..008f71d6d1 100644 --- a/src/slic3r/GUI/ParamsDialog.cpp +++ b/src/slic3r/GUI/ParamsDialog.cpp @@ -3,6 +3,7 @@ #include "ParamsPanel.hpp" #include "GUI_App.hpp" #include "MainFrame.hpp" +#include "Plater.hpp" #include "Tab.hpp" #include "libslic3r/Utils.hpp" @@ -70,6 +71,18 @@ ParamsDialog::ParamsDialog(wxWindow * parent) } Hide(); + if (tab && tab->type() == Preset::TYPE_PRINTER) { + // Normalize only after the dialog closes, when the final capability is known. + auto &preset_bundle = *wxGetApp().preset_bundle; + const bool supports_multiple_bed_types = preset_bundle.is_bbl_vendor() || + preset_bundle.printers.get_edited_preset().config.opt_bool("support_multi_bed_types"); + if (m_initial_multi_bed_types != supports_multiple_bed_types) { + wxGetApp().plater()->normalize_bed_types(true); + if (auto *plate_tab = dynamic_cast(wxGetApp().get_plate_tab())) + plate_tab->update_model_config(); + } + } + if (!m_editing_filament_id.empty()) { Filamentinformation *filament_info = new Filamentinformation(); filament_info->filament_id = m_editing_filament_id; @@ -93,7 +106,15 @@ void ParamsDialog::Popup() if (m_panel && m_panel->get_current_tab()) { bool just_edit = false; if (!m_editing_filament_id.empty()) just_edit = true; - dynamic_cast(m_panel->get_current_tab())->set_just_edit(just_edit); + auto *tab = dynamic_cast(m_panel->get_current_tab()); + tab->set_just_edit(just_edit); + if (tab->type() == Preset::TYPE_PRINTER) { + // Remember the initial capability and compare it when the dialog closes. + // Bambu profiles support multiple bed types even when this option is unset. + auto &preset_bundle = *wxGetApp().preset_bundle; + m_initial_multi_bed_types = preset_bundle.is_bbl_vendor() || + preset_bundle.printers.get_edited_preset().config.opt_bool("support_multi_bed_types"); + } } Show(); } diff --git a/src/slic3r/GUI/ParamsDialog.hpp b/src/slic3r/GUI/ParamsDialog.hpp index 9ad34e165e..80c9c3d2fe 100644 --- a/src/slic3r/GUI/ParamsDialog.hpp +++ b/src/slic3r/GUI/ParamsDialog.hpp @@ -43,6 +43,7 @@ protected: private: std::string m_editing_filament_id; + bool m_initial_multi_bed_types = false; ParamsPanel * m_panel; wxWindowDisabler *m_winDisabler = nullptr; }; diff --git a/src/slic3r/GUI/PlateSettingsDialog.cpp b/src/slic3r/GUI/PlateSettingsDialog.cpp index b640b2cb84..31b8367774 100644 --- a/src/slic3r/GUI/PlateSettingsDialog.cpp +++ b/src/slic3r/GUI/PlateSettingsDialog.cpp @@ -431,8 +431,9 @@ PlateSettingsDialog::PlateSettingsDialog(wxWindow* parent, const wxString& title } } - if (!wxGetApp().preset_bundle->is_bbl_vendor()) - m_bed_type_choice->Disable(); + auto &preset_bundle = *wxGetApp().preset_bundle; + const auto &printer_config = preset_bundle.printers.get_edited_preset().config; + m_bed_type_choice->Enable(preset_bundle.is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types")); wxStaticText* m_bed_type_txt = new wxStaticText(this, wxID_ANY, _L("Bed type")); m_bed_type_txt->SetFont(Label::Body_14); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 89616345eb..43a8b9a6a1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -7401,7 +7401,8 @@ struct Plater::priv std::vector load_files(const std::vector& input_files, LoadStrategy strategy, bool ask_multi = false, - bool* published_out = nullptr); + bool* published_out = nullptr, + bool* config_loaded_out = nullptr); std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool split_object = false, bool auto_drop = true); // Texture-to-color import: a mesh loaded with UVs + a texture map gets its faces clustered @@ -8872,8 +8873,12 @@ void read_binary_stl(const std::string& filename, std::string& model_id, std::st std::vector Plater::priv::load_files(const std::vector& input_files, LoadStrategy strategy, bool ask_multi, - bool* published_out) + bool* published_out, + bool* config_loaded_out) { + if (config_loaded_out != nullptr) + *config_loaded_out = false; + std::vector empty_result; bool dlg_cont = true; bool is_user_cancel = false; @@ -9558,6 +9563,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ id = agent->to_orca_filament_id(id); } preset_bundle->load_config_model(filename.string(), std::move(config), file_version, &published_config); + if (config_loaded_out != nullptr) + *config_loaded_out = true; // Mixed-filament definitions that collided with one of the // receiver's real slots were relocated during the preset load. @@ -12780,6 +12787,8 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt) // update plater with new config q->on_config_change(wxGetApp().preset_bundle->full_config()); + if (auto *plate_tab = dynamic_cast(wxGetApp().get_plate_tab())) + plate_tab->update_model_config(); // update app_config AppConfig* app_config = wxGetApp().app_config; @@ -15479,6 +15488,38 @@ void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_init void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW +void Plater::normalize_bed_types(bool printer_setting_changed) +{ + if (only_gcode_mode() || is_gcode_3mf()) + return; + + auto &preset_bundle = *wxGetApp().preset_bundle; + // Keep FFF plate settings intact while an SLA printer is selected. + if (preset_bundle.printers.get_edited_preset().printer_technology() != ptFFF) + return; + + const auto &printer_config = preset_bundle.printers.get_edited_preset().config; + + const bool supports_multiple_bed_types = + preset_bundle.is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types"); + // Clear local overrides for single-bed printers. + const bool overrides_reset = !supports_multiple_bed_types && + !p->partplate_list.check_all_plate_local_bed_type({}); + + if (overrides_reset) { + set_plater_dirty(true); + show_info(this, + _L("The selected printer does not support multiple bed types.\nBed type overrides were reset to the global bed type."), + _L("Plate bed types reset")); + } + + // Refresh the controls after a capability change, even when no override was reset. + if (printer_setting_changed || overrides_reset) { + sidebar().update_all_preset_comboboxes(); + wxGetApp().obj_list()->update_and_show_object_settings_item(); + } +} + std::vector Plater::mixed_filament_config_indices() const { std::vector indices; @@ -16059,6 +16100,9 @@ void Plater::load_project(wxString const& filename2, p->dirty_state.update_from_undo_redo_stack(true); up_to_date(true, true); + // Clear plate overrides that are incompatible with the selected printer. + normalize_bed_types(false); + wxGetApp().params_panel()->switch_to_object_if_has_object_configs(); auto has_modify = is_flush_config_modified(); @@ -17691,7 +17735,11 @@ std::vector Plater::load_files(const std::vector& input_files, p->m_slice_all_only_has_gcode = false; //BBS: wish to reset all plates stats item selected state when load a new file p->preview->get_canvas3d()->reset_select_plate_toolbar_selection(); - return p->load_files(input_files, strategy, ask_multi, published_out); + bool config_loaded = false; + std::vector result = p->load_files(input_files, strategy, ask_multi, published_out, &config_loaded); + if (config_loaded && !is_loading_project()) + normalize_bed_types(false); + return result; } bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a887805f8a..17534ebf7d 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -343,6 +343,7 @@ public: bool is_presets_dirty() const; void set_plater_dirty(bool is_dirty); void update_project_dirty_from_presets(); + void normalize_bed_types(bool printer_setting_changed); int save_project_if_dirty(const wxString& reason); void reset_project_dirty_after_save(); void reset_project_dirty_initial_presets(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c60566d5c7..3050830d56 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -87,6 +87,7 @@ #include "slic3r/plugin/PluginConfig.hpp" #include "slic3r/plugin/PluginManager.hpp" #include "Plater.hpp" +#include "ParamsDialog.hpp" #include "MainFrame.hpp" #include "format.hpp" #include "UnsavedChangesDialog.hpp" @@ -3302,6 +3303,11 @@ void TabPrint::toggle_options() m_config_manipulation.toggle_print_fff_options(m_config, int(intptr_t(m_extruder_switch->GetClientData())), m_type < Preset::TYPE_COUNT); + if (m_type == Preset::TYPE_PLATE) { + const auto &printer_config = m_preset_bundle->printers.get_edited_preset().config; + toggle_option("curr_bed_type", m_preset_bundle->is_bbl_vendor() || printer_config.opt_bool("support_multi_bed_types")); + } + Field *field = m_active_page->get_field("support_style"); auto support_type = m_config->opt_enum("support_type"); if (auto choice = dynamic_cast(field)) { @@ -7024,6 +7030,15 @@ bool Tab::select_preset( } load_current_preset(); + // Wait for the settings dialog to close; its edits are still provisional. + if (printer_tab && is_selected) { + Plater *plater = wxGetApp().plater(); + ParamsDialog *dialog = wxGetApp().params_dialog(); + if (plater && !plater->is_loading_project() && + (!dialog || !dialog->IsShown())) + plater->normalize_bed_types(false); + } + { Slic3r::LifecycleEventContext ctx; ctx.name = preset_name; diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index 4f1998ae94..350dd9d4be 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -71,6 +71,7 @@ #include #include "MainFrame.hpp" +#include "Plater.hpp" #include #include #include @@ -1161,6 +1162,8 @@ bool GuideFrame::run() app.app_config->set_legacy_datadir(false); app.update_mode(); + if (Plater *plater = app.plater()) + plater->normalize_bed_types(false); // BBS //app.obj_manipul()->update_ui_from_settings(); BOOST_LOG_TRIVIAL(info) << "GuideFrame applied"; From 00bb4202fe50ca625e59c6fcbec573a2887d22ed Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sat, 3 Oct 2026 03:12:31 +0300 Subject: [PATCH 3/3] Fix gizmo checkbox contrast; align Texture Displacement styling and panel refresh behavior (#16076) * Fix gizmo checkbox styling and Texture Displacement resize Use shared BBL checkboxes in Texture Displacement and restore white toolbar checkmarks so other gizmos keep proper checkbox contrast. Also fix Texture Displacement resizing only after mouse movement by requesting additional frames while its layout is still changing. * Fix gizmo checkbox styling and Texture Displacement resize Use shared BBL checkboxes in Texture Displacement and restore white toolbar checkmarks so other gizmos keep proper checkbox contrast. Also fix Texture Displacement resizing only after mouse movement by requesting additional frames while its layout is still changing. * Update GLGizmoTextureDisplacement.cpp --------- Co-authored-by: yw4z --- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 80 ++++++++++--------- src/slic3r/GUI/ImGuiWrapper.cpp | 6 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 4aed58d884..fe1368b69e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -5151,7 +5151,7 @@ void GLGizmoTextureDisplacement::render_debug_stage_panel(ModelVolume *mv) m_imgui->scaled(20.f)); ImGui::SameLine(); - ImGui::Checkbox(_u8L("Check topology").c_str(), &m_debug_check_topology); + m_imgui->bbl_checkbox(_L("Check topology"), m_debug_check_topology); if (ImGui::IsItemHovered()) m_imgui->tooltip(_u8L("Count open and non-manifold edges after each stage, which is how a stage " "that tore the mesh is spotted. It scans every edge, so it adds noticeably " @@ -5341,6 +5341,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float GizmoImguiBegin(get_name(), flags); ensure_panel_icons(); process_uv_commands(); // clicks from the UV editor pane, run here where the GL context is current + const float previous_body_h = m_panel_body_h; + const float previous_footer_h = m_panel_footer_h; // Pinned every frame while Standard is active, so what Preview shows is always what Bake will do. if (!pro_mode() && apply_standard_mode_presets(mv)) @@ -5362,20 +5364,16 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const float card_pad = std::round(m_imgui->scaled(0.55f)); const float wrap_w = m_imgui->scaled(20.f); const ImVec4 orca = ImGuiWrapper::COL_ORCA; - const ImVec4 col_link = dark ? ImVec4(0.30f, 0.71f, 0.67f, 1.f) : ImVec4(0.f, 0.47f, 0.42f, 1.f); - const ImVec4 col_frame = dark ? ImVec4(0.212f, 0.212f, 0.235f, 1.f) : ImVec4(0.808f, 0.808f, 0.808f, 1.f); - const ImU32 col_card = dark ? IM_COL32(255, 255, 255, 10) : IM_COL32(0, 0, 0, 12); - const ImU32 col_line = dark ? IM_COL32(255, 255, 255, 18) : IM_COL32(0, 0, 0, 23); + const ImVec4 col_link = ImGuiWrapper::COL_ORCA; + const ImVec4 col_frame = ImGui::GetStyleColorVec4(ImGuiCol_Separator); + const ImU32 col_card = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.045f : 0.047f); + const ImU32 col_line = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.08f : 0.09f); const ImU32 col_sep = ImGui::GetColorU32(ImGuiCol_Separator); - // Combo drop-downs otherwise inherit ImGui's near-black default popup background; under the light - // theme that leaves the dark item text unreadable ("the dropbox is black"). Pushed only around each - // Combo below (never around a tooltip, whose own near-black default is what makes it readable). - const ImVec4 combo_popup_bg = dark ? ImVec4(0.18f, 0.18f, 0.19f, 1.f) : ImVec4(0.93f, 0.93f, 0.93f, 1.f); const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) { - ImGui::PushStyleColor(ImGuiCol_PopupBg, combo_popup_bg); + ImGuiWrapper::push_combo_style(m_parent.get_scale()); const bool changed = ImGui::Combo(id, v, items, n); - ImGui::PopStyleColor(); + ImGuiWrapper::pop_combo_style(); return changed; }; const auto hover_tip = [&](const auto &text) { @@ -5782,10 +5780,10 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float wf_toggle = true; const std::string auto_label = _u8L("Auto"); - const float auto_w = frame_h + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x; + const float auto_w = frame_h * 0.78f /*ratio from BBLCheckbox*/ + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x; ImGui::SameLine(); ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - auto_w)); - if (ImGui::Checkbox((auto_label + "##auto_update").c_str(), &m_auto_update) && m_auto_update) + if (m_imgui->bbl_checkbox(wxString::FromUTF8(auto_label) + "##auto_update", m_auto_update) && m_auto_update) rebuild_preview(); // catch up anything that changed while it was off hover_tip(_u8L("Rebuilds the preview as soon as anything changes. Turn it off on a heavy model if painting " "or dragging a slider starts to stutter - the preview then waits until you let go.")); @@ -6020,7 +6018,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "costs fine detail.")); // Edge fade: the relief flattens toward the boundary of the painted area. - m_preview_params_dirty |= ImGui::Checkbox((_u8L("Edge fade") + "##edge_smoothing").c_str(), &layer.edge_smoothing); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Edge fade") + "##edge_smoothing", layer.edge_smoothing); hover_tip(_u8L("Flattens the relief as it approaches the edge of the painted area, so it " "blends into the bare surface instead of stopping at a step.")); ImGui::SameLine(); @@ -6035,7 +6033,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Invert and Colours share a row. { const float x0 = ImGui::GetCursorPosX(); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Invert").c_str(), &layer.invert); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Invert"), layer.invert); hover_tip(_u8L("Turns the relief inside out: what stood out is cut in, and the other way " "round. The same as using a negative of the image.")); ImGui::SameLine(); @@ -6047,7 +6045,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const bool has_color = decode_height_texture(layer).has_color(); bool color_enabled = layer.color_enabled && has_color; m_imgui->disabled_begin(!has_color); - if (ImGui::Checkbox(_u8L("Colours").c_str(), &color_enabled)) { + if (m_imgui->bbl_checkbox(_L("Colours"), color_enabled)) { layer.color_enabled = color_enabled; m_preview_params_dirty = true; } @@ -6065,7 +6063,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // under whichever layer turned colour on. if (color_enabled) { TextureDisplacementOptions &opts = mv->texture_displacement_options; - if (ImGui::Checkbox(_u8L("Mix filaments").c_str(), &opts.color_mix_enabled)) + if (m_imgui->bbl_checkbox(_L("Mix filaments"), opts.color_mix_enabled)) m_preview_params_dirty = true; hover_tip(_u8L("Interleaves two filaments to fake the colours in between, so a handful " "of filaments can cover a photo or a gradient. An image of flat colours " @@ -6184,7 +6182,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "a slide projector. Faces turned away from you stretch, so line the view up with " "the surface you care about first.")); - if (ImGui::Checkbox(_u8L("Project only on visible").c_str(), &m_project_only_visible)) { + if (m_imgui->bbl_checkbox(_L("Project only on visible"), m_project_only_visible)) { if (m_project_only_visible && select_visible_faces() == 0) show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the " "part you want to project onto.")); @@ -6195,7 +6193,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float "behind anything - and projects onto those. Replaces what the layer had painted.")); bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown(); - if (ImGui::Checkbox(_u8L("Projection frame").c_str(), &projector_open)) + if (m_imgui->bbl_checkbox(_L("Projection frame"), projector_open)) show_projector(projector_open); hover_tip(_u8L("Opens a window you drag over the model. Whatever you can see through it is what " "gets the texture, and its border becomes the edge of the projection.")); @@ -6236,7 +6234,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // Tile, and how it repeats. { const float x0 = ImGui::GetCursorPosX(); - m_preview_params_dirty |= ImGui::Checkbox((_u8L("Tile") + "##tile_enabled").c_str(), &layer.tile_enabled); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Tile") + "##tile_enabled", layer.tile_enabled); hover_tip(_u8L("Repeats the texture across the painted area. Off places one copy, like a decal, " "at the size set by Tile size.")); ImGui::SameLine(); @@ -6424,7 +6422,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const ImVec2 mn(mouse.x - inner - 0.5f * grip_w, mouse.y - 0.5f * row_h); const ImVec2 mx(mn.x + (content_rx - list_x), mn.y + row_h); fg->AddRectFilled(ImVec2(mn.x + 2.f, mn.y + 4.f), ImVec2(mx.x + 2.f, mx.y + 4.f), IM_COL32(0, 0, 0, 70), rounding); - fg->AddRectFilled(mn, mx, dark ? IM_COL32(0x3a, 0x3a, 0x40, 245) : IM_COL32(255, 255, 255, 245), rounding); + fg->AddRectFilled(mn, mx, ImGui::GetColorU32(ImGuiCol_WindowBg, 245.f / 255.f), rounding); fg->AddRect(mn, mx, ImGui::GetColorU32(orca), rounding, 0, 1.5f); float x = mn.x + inner; @@ -6463,7 +6461,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::Separator(); heading(_L("Subdivision")); - if (ImGui::Checkbox(_u8L("Only painted area (adaptive)").c_str(), &m_subdivide_adaptive)) { + if (m_imgui->bbl_checkbox(_L("Only painted area (adaptive)"), m_subdivide_adaptive)) { if (m_subdivide_editing) rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result m_parent.set_as_dirty(); @@ -6494,7 +6492,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_parent.set_as_dirty(); }; - if (ImGui::Checkbox(_u8L("Follow texture detail").c_str(), &m_subdivide_feature)) + if (m_imgui->bbl_checkbox(_L("Follow texture detail"), m_subdivide_feature)) preview_live(); hover_tip(_u8L("Spends the triangles where the texture actually bends - packed along ridges and edges, " "sparse over flat ground - instead of spreading them evenly. The same detail for fewer " @@ -6621,7 +6619,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float hover_tip(_u8L("Triangle size the whole model is rebuilt with, in millimetres. Displacement works best on an " "even mesh; this is what makes one out of an uneven import.")); - ImGui::Checkbox((_u8L("Keep sharp edges") + "##remesh_sharp").c_str(), &m_remesh_keep_sharp_edges); + m_imgui->bbl_checkbox(_L("Keep sharp edges") + "##remesh_sharp", m_remesh_keep_sharp_edges); hover_tip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it " "the remesher slides vertices along the surface and rounds every crisp edge off - " "a cube comes back with wobbly edges.")); @@ -6648,12 +6646,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::Separator(); heading(_L("Result")); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Displace up to the border").c_str(), &opts.displace_border); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Displace up to the border"), opts.displace_border); hover_tip(_u8L("Lets the relief run right to the edge of the painted area. Turn it off to hold that " "outer ring flat, which keeps the displacement strictly inside your paint but flattens " "the pattern at the border.")); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Smooth result").c_str(), &opts.smooth_enabled); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Smooth result"), opts.smooth_enabled); hover_tip(_u8L("Smooths the geometry after the texture has been applied, to take the hard steps out of a " "low-resolution image. Only what the displacement moved is touched. The Smoothing slider " "on a layer is a different thing: it blurs the image before it is used.")); @@ -6670,7 +6668,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float hover_tip(_u8L("How many smoothing passes to run. More passes spread the smoothing further across " "the surface; Strength decides how much each one moves.")); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Ignore outer ring").c_str(), &opts.smooth_skip_border); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Ignore outer ring"), opts.smooth_skip_border); hover_tip(_u8L("Keeps the outer ring of the painted area out of the smoothing. Its neighbours " "outside the paint never move, so smoothing it drags the relief down and leaves the " "pattern half-melted at the border.")); @@ -6695,7 +6693,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // The classic path is the opt-in: the one-run pipeline is the default, and its resolution // control lives in the footer next to Bake (see below). bool classic = !opts.pipeline_v2; - if (ImGui::Checkbox(_u8L("Experimental: classic bake pipeline").c_str(), &classic)) { + if (m_imgui->bbl_checkbox(_L("Experimental: classic bake pipeline"), classic)) { opts.pipeline_v2 = !classic; m_preview_params_dirty = true; } @@ -6724,13 +6722,13 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float if (SHOW_PIPELINE_DEV_CONTROLS && opts.pipeline_v2) { // Keeping the relief above the plate is not a checkbox: it is unconditional, in both pipelines // (see build_texture_displacement()). - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Align mesh to texture edges").c_str(), &opts.v2_relocate); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Align mesh to texture edges"), opts.v2_relocate); hover_tip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. " "Displacement can only move vertices up and down, so without this a sharp step " "in the image lands wherever the triangles happen to be and comes out as a " "staircase. Moving the vertices onto the step first gives a straight wall at the " "same triangle count.")); - m_preview_params_dirty |= ImGui::Checkbox(_u8L("Clean up slivers").c_str(), &opts.v2_regularize); + m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Clean up slivers"), opts.v2_regularize); hover_tip(_u8L("Collapse the thin triangles refinement inherits from the model's own " "tessellation, before displacement samples them. A sliver's three corners " "land on three unrelated parts of the texture, which is what makes the " @@ -6810,7 +6808,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float // a fixed value, seeded with the recommendation so it starts from something sensible. bool auto_res = opts.v2_refine_mm <= 0.f; m_imgui->disabled_begin(busy); - if (ImGui::Checkbox("##v2auto", &auto_res)) { + if (m_imgui->bbl_checkbox(wxString::FromUTF8("##v2auto"), auto_res)) { if (auto_res) { opts.v2_refine_mm = 0.f; opts.v2_max_triangles_k = -1; @@ -6908,11 +6906,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const std::string bake_label = m_prepare_in_progress ? _u8L("Preparing...") : m_bake_in_progress ? _u8L("Baking...") : into_u8(m_desc.at("bake")); - ImGui::PushStyleColor(ImGuiCol_Button, orca); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGuiWrapper::COL_ORCA_HOVER); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, orca); - ImGui::PushStyleColor(ImGuiCol_Border, orca); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f)); + GLGizmoUtils::push_orca_button_style(); m_imgui->push_bold_font(); m_imgui->disabled_begin(!can_bake); if (ImGui::Button((bake_label + "##bake").c_str(), ImVec2(x0 + panel_w - ImGui::GetCursorPosX(), button_h))) { @@ -6925,7 +6919,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } m_imgui->disabled_end(); m_imgui->pop_bold_font(); - ImGui::PopStyleColor(5); + GLGizmoUtils::pop_orca_button_style(); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) m_imgui->tooltip(mv != nullptr && !mv->is_texture_displacement_painted() ? (m_seam_edit_mode ? _u8L("Nothing is painted yet. The UV editor's seam tool is on, so " @@ -6964,7 +6958,17 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } } m_panel_footer_h = ImGui::GetCursorScreenPos().y - footer_top; - + constexpr float resize_epsilon = 0.5f; + const float content_bottom = ImGui::GetCursorPosY() - style.ItemSpacing.y; + const float window_bottom = ImGui::GetWindowContentRegionMax().y; + const bool outer_window_needs_fit = content_bottom < window_bottom - resize_epsilon || + (content_bottom > window_bottom + resize_epsilon && + ImGui::GetWindowHeight() < ImGui::GetMainViewport()->Size.y - + 2.f * style.DisplaySafeAreaPadding.y - resize_epsilon); + if (std::abs(m_panel_body_h - previous_body_h) > resize_epsilon || + std::abs(m_panel_footer_h - previous_footer_h) > resize_epsilon || outer_window_needs_fit) { + m_parent.request_extra_frame(); + } GizmoImguiEnd(); ImGuiWrapper::pop_toolbar_style(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 64bb5e11ca..41663d147e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -2656,11 +2656,7 @@ void ImGuiWrapper::push_toolbar_style(const float scale) ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.00f)); // 10 ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); // 11 ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, COL_GREEN_LIGHT); // 12 - // The checkbox/radio frame behind this is drawn fully transparent (see FrameBg above, - // alpha 0), showing the light window background through it - a white check mark there is - // invisible. Dark mode doesn't have this problem (its window background is dark), so only - // this branch needs a check mark color with real contrast against a light background. - ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.f, 156 / 255.f, 136 / 255.f, 1.00f));//13 + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));//13 ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f));