diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 747567fe5c..2fd4d67983 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1941,7 +1941,8 @@ sub selection_changed { $self->{object_info_manifold}->SetToolTipString($message); $self->{object_info_manifold_warning_icon}->SetToolTipString($message); } else { - $self->{object_info_manifold}->SetLabel(L("Yes")); + $self->{object_info_manifold}->SetLabel(L("Yes")); + $self->{object_info_manifold_warning_icon}->Hide; } } else { $self->{object_info_facets}->SetLabel($object->facets); diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 0a531849da..a4eaf3072a 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -206,6 +206,18 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const return diff; } +t_config_option_keys ConfigBase::equal(const ConfigBase &other) const +{ + t_config_option_keys equal; + for (const t_config_option_key &opt_key : this->keys()) { + const ConfigOption *this_opt = this->option(opt_key); + const ConfigOption *other_opt = other.option(opt_key); + if (this_opt != nullptr && other_opt != nullptr && *this_opt == *other_opt) + equal.emplace_back(opt_key); + } + return equal; +} + std::string ConfigBase::serialize(const t_config_option_key &opt_key) const { const ConfigOption* opt = this->option(opt_key); diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 1f4e85a349..6eb307c5ce 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -1046,6 +1046,7 @@ public: void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false); bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } t_config_option_keys diff(const ConfigBase &other) const; + t_config_option_keys equal(const ConfigBase &other) const; std::string serialize(const t_config_option_key &opt_key) const; // Set a configuration value from a string, it will call an overridable handle_legacy() // to resolve renamed and removed configuration keys. diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 984aa05ae2..1d5a25e065 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -28,12 +28,15 @@ namespace Slic3r { namespace GUI { } m_Undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); - m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); - m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ /*on_back_to_initial_value()*/; })); + m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ /*on_back_to_sys_value()*/; })); BUILD(); } + void Field::set_nonsys_btn_icon(const std::string& icon){ + m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG)); + } + void Field::on_kill_focus(wxEvent& event) { // Without this, there will be nasty focus bugs on Windows. // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index b5a4c66faf..2a1f707a7c 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -68,8 +68,8 @@ public: // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; - // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_is_modified_value {false}; + bool m_is_nonsys_value; /// Copy of ConfigOption for deduction purposes const ConfigOptionDef m_opt {ConfigOptionDef()}; @@ -96,6 +96,9 @@ public: virtual wxString get_tooltip_text(const wxString& default_string); + // set icon to "UndoToSystemValue" button according to an inheritance of preset + void set_nonsys_btn_icon(const std::string& icon); + Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {}; Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {}; diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 262d41a799..fdb97ef72e 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -518,11 +518,14 @@ wxApp* get_app(){ return g_wxApp; } -wxColour* get_modified_label_clr() -{ +wxColour* get_modified_label_clr(){ return new wxColour(253, 88, 0); } +wxColour* get_sys_label_clr(){ + return new wxColour(26, 132, 57); +} + void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) { if (comboCtrl == nullptr) diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 2baa10cb95..75b54652ba 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -79,6 +79,7 @@ void set_preset_bundle(PresetBundle *preset_bundle); AppConfig* get_app_config(); wxApp* get_app(); wxColour* get_modified_label_clr(); +wxColour* get_sys_label_clr(); void add_debug_menu(wxMenuBar *menu, int event_language_change); diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index d672d4b479..7faf9baefc 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -86,6 +86,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co field->m_Undo_btn->Hide(); field->m_Undo_to_sys_btn->Hide(); } + if (nonsys_btn_icon != "") + field->set_nonsys_btn_icon(nonsys_btn_icon); // assign function objects for callbacks, etc. return field; @@ -115,8 +117,10 @@ void OptionsGroup::append_line(const Line& line) { const auto& option = option_set.front(); const auto& field = build_field(option); - sizer->Add(field->m_Undo_to_sys_btn); - sizer->Add(field->m_Undo_btn); + auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); + btn_sizer->Add(field->m_Undo_to_sys_btn); + btn_sizer->Add(field->m_Undo_btn); + sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0); if (is_window_field(field)) sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); if (is_sizer_field(field)) diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp index 42db222254..0fe162b7a0 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/xs/src/slic3r/GUI/OptionsGroup.hpp @@ -83,6 +83,8 @@ public: wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; + std::string nonsys_btn_icon = ""; + /// Returns a copy of the pointer of the parent wxWindow. /// Accessor function is because users are not allowed to change the parent /// but defining it as const means a lot of const_casts to deal with wx functions. diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 661b11ee0d..a5ab65c9fe 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -226,7 +226,8 @@ const std::vector& Preset::printer_options() "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "default_print_profile", "inherits", + "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "max_print_height", + "default_print_profile", "inherits", }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); } @@ -411,7 +412,7 @@ const Preset* PresetCollection::get_selected_preset_parent() const { auto *inherits = dynamic_cast(this->get_edited_preset().config.option("inherits")); if (inherits == nullptr || inherits->value.empty()) - return nullptr; + return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; // nullptr; const Preset* preset = this->find_preset(inherits->value, false); return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset; } @@ -576,6 +577,25 @@ std::vector PresetCollection::dirty_options(const Preset *edited, c return changed; } +std::vector PresetCollection::system_equal_options() const +{ + const Preset *edited = &this->get_edited_preset(); + const Preset *reference = this->get_selected_preset_parent(); + std::vector equal; + if (edited != nullptr && reference != nullptr) { + equal = reference->config.equal(edited->config); + // The "compatible_printers" option key is handled differently from the others: + // It is not mandatory. If the key is missing, it means it is compatible with any printer. + // If the key exists and it is empty, it means it is compatible with no printer. + std::initializer_list optional_keys{ "compatible_printers", "compatible_printers_condition" }; + for (auto &opt_key : optional_keys) { + if (reference->config.has(opt_key) == edited->config.has(opt_key)) + equal.emplace_back(opt_key); + } + } + return equal; +} + // Select a new preset. This resets all the edits done to the currently selected preset. // If the preset with index idx does not exist, a first visible preset is selected. Preset& PresetCollection::select_preset(size_t idx) diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index a6ce77dfe0..3634c5dd94 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -250,6 +250,8 @@ public: // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. std::vector current_different_from_parent_options() const { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent()); } + // Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal. + std::vector system_equal_options() const; // Update the choice UI from the list of presets. // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 4a4ba1d73c..894e593c61 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -113,6 +113,14 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) update(); } +void Tab::load_initial_data() +{ + m_config = &m_presets->get_edited_preset().config; + m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ? + "bullet_white.png" : + wxMSW ? "sys_unlock.png" : "lock_open.png"; +} + PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder_pages/* = false*/) { // Index of icon in an icon list $self->{icons}. @@ -186,17 +194,19 @@ void Tab::update_changed_ui() // Add new dirty options to m_dirty_options for (auto opt_key : dirty_options){ Field* field = get_field(opt_key); - if (field != nullptr && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()){ - if (field->m_Label != nullptr){ - field->m_Label->SetForegroundColour(*get_modified_label_clr()); - field->m_Label->Refresh(true); - } + if (field != nullptr && + find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()){ // use bouth of temporary_icons till don't have "undo_icon" field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(wxMSW ? var("action_undo.png") : var("arrow_undo.png")), wxBITMAP_TYPE_PNG)); field->m_is_modified_value = true; m_dirty_options.push_back(opt_key); } + + if (field != nullptr && field->m_Label != nullptr){ + field->m_Label->SetForegroundColour(*get_modified_label_clr()); + field->m_Label->Refresh(true); + } } // Delete clear options from m_dirty_options @@ -219,6 +229,47 @@ void Tab::update_changed_ui() } } } + + + //update system options (colored in green) + auto sys_options = m_presets->system_equal_options(); + // Add new system equal options to m_sys_options + for (auto opt_key : sys_options){ + Field* field = get_field(opt_key); + if (field != nullptr && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()){ + field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(wxMSW ? var("sys_lock.png") : var("lock.png")), wxBITMAP_TYPE_PNG)); + field->m_is_nonsys_value = false; + + m_sys_options.push_back(opt_key); + } + if (field != nullptr && field->m_Label != nullptr){ + field->m_Label->SetForegroundColour(*get_sys_label_clr()); + field->m_Label->Refresh(true); + } + } + // Delete clear options from m_dirty_options + for (auto i = 0; i < m_sys_options.size(); ++i) + { + const std::string &opt_key = m_sys_options[i]; + Field* field = get_field(opt_key); + if (field != nullptr && find(sys_options.begin(), sys_options.end(), opt_key) == sys_options.end()) + { + // use bouth of temporary_icons till don't have "unlock_icon" + field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG)); + if (field->m_Label != nullptr && + find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()){ + field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT); + field->m_Label->Refresh(true); + } + field->m_is_nonsys_value = true; + std::vector::iterator itr = find(m_sys_options.begin(), m_sys_options.end(), opt_key); + if (itr != m_sys_options.end()){ + m_sys_options.erase(itr); + --i; + } + } + } + } // Update the combo box label of the selected preset based on its "dirty" state, @@ -376,7 +427,7 @@ void Tab::reload_compatible_printers_widget() void TabPrint::build() { m_presets = &m_preset_bundle->prints; - m_config = &m_presets->get_edited_preset().config; + load_initial_data(); auto page = add_options_page(_(L("Layers and perimeters")), "layers.png"); auto optgroup = page->new_optgroup(_(L("Layer height"))); @@ -853,7 +904,7 @@ void TabPrint::OnActivate() void TabFilament::build() { m_presets = &m_preset_bundle->filaments; - m_config = &m_preset_bundle->filaments.get_edited_preset().config; + load_initial_data(); auto page = add_options_page(_(L("Filament")), "spool.png"); auto optgroup = page->new_optgroup(_(L("Filament"))); @@ -1000,8 +1051,7 @@ bool Tab::current_preset_is_dirty() void TabPrinter::build() { m_presets = &m_preset_bundle->printers; - m_config = &m_preset_bundle->printers.get_edited_preset().config; - auto default_config = m_preset_bundle->full_config(); + load_initial_data(); auto *nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); @@ -1414,6 +1464,9 @@ void TabPrinter::update(){ void Tab::load_current_preset() { auto preset = m_presets->get_edited_preset(); + m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ? + "bullet_white.png" : + wxMSW ? "sys_unlock.png" : "lock_open.png"; preset.is_default ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); update(); // For the printer profile, generate the extruder pages. @@ -1658,6 +1711,8 @@ void Tab::save_preset(std::string name /*= ""*/) update_tab_ui(); // Update the selection boxes at the platter. on_presets_changed(); + + update_changed_ui(); } // Called for a currently selected preset. @@ -1833,6 +1888,8 @@ ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_wid return config; }; + optgroup->nonsys_btn_icon = static_cast(GetParent())->m_nonsys_btn_icon; + vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10); m_optgroups.push_back(optgroup); diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index e2dc51ee4e..d3be0e8bc5 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -100,6 +100,7 @@ protected: std::vector m_reload_dependent_tabs = {}; std::vector m_dirty_options = {}; + std::vector m_sys_options = {}; // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. wxEventType m_event_value_change = 0; @@ -110,6 +111,7 @@ public: bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; + std::string m_nonsys_btn_icon; public: Tab() {} @@ -154,6 +156,7 @@ public: virtual void on_preset_loaded(){} virtual void build() = 0; virtual void update() = 0; + void load_initial_data(); void update_dirty(); void update_tab_ui(); void load_config(DynamicPrintConfig config);