From 016b5e35b6391e9463bcb99a1126908654129b6e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Mar 2019 10:54:56 +0100 Subject: [PATCH 1/4] Some _cosmetic_ fixes : + Set wxDEFAULT_DIALOG_STYLE for MsgDialog + Changed Msg text before language changing --- src/slic3r/GUI/GUI_App.cpp | 16 ++++++---------- src/slic3r/GUI/MsgDialog.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 177f924c7..bdfd2fc26 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -649,20 +649,16 @@ void GUI_App::add_config_menu(wxMenuBar *menu) } case ConfigMenuLanguage: { - /* Before change application language, let's check unsaved changes + /* Before change application language, let's check unsaved changes on 3D-Scene * and draw user's attention to the application restarting after a language change */ wxMessageDialog dialog(nullptr, - _(L("Application will be restarted after language change, " - "and 3D-Scene will be cleaned.")) + "\n" + - _(L("Please, check your changes before.")) + "\n\n" + - _(L("Continue anyway?")), + _(L("Application will be restarted after language change.")) + "\n" + + _(L("3D-Scene will be cleaned.")) + "\n\n" + + _(L("Please, check your changes before.")), _(L("Attention!")), - wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - if ( dialog.ShowModal() != wxID_YES) - return; - - if (!wxGetApp().check_unsaved_changes()) + wxICON_QUESTION | wxOK | wxCANCEL); + if ( dialog.ShowModal() == wxID_CANCEL) return; wxArrayString names; diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 961888455..cc2b9c842 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -28,7 +28,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he {} MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) : - wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER), +wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)), content_sizer(new wxBoxSizer(wxVERTICAL)), btn_sizer(new wxBoxSizer(wxHORIZONTAL)) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fa4587ca6..6db5a5d5b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -427,7 +427,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : option = m_og->get_option("fill_density"); option.opt.label = L("Infill"); - option.opt.width = 5 * wxGetApp().em_unit(); + option.opt.width = 6 * wxGetApp().em_unit(); option.opt.sidetext = " "; line.append_option(option); From 44d3243df1fc46681b2b1d9ee0db5cd790c031ac Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Mar 2019 16:28:34 +0100 Subject: [PATCH 2/4] Added context menu Item "Change Extruder" on 3D-Scene --- src/slic3r/GUI/GUI_ObjectList.cpp | 79 ++++++++++++++++++++++++------- src/slic3r/GUI/GUI_ObjectList.hpp | 6 ++- src/slic3r/GUI/Plater.cpp | 50 ++++++++++--------- src/slic3r/GUI/wxExtensions.cpp | 18 +++++++ src/slic3r/GUI/wxExtensions.hpp | 6 ++- 5 files changed, 117 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 642a9fb55..093d77814 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -228,6 +228,21 @@ int ObjectList::get_selected_obj_idx() const return -1; } +DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) const +{ + assert(item); + const ItemType type = m_objects_model->GetItemType(item); + + const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : + m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); + + const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; + + assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0)); + return type & itObject|itInstance ? (*m_objects)[obj_idx]->config : + (*m_objects)[obj_idx]->volumes[vol_idx]->config; +} + wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_count) { wxArrayString choices; @@ -910,8 +925,10 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu) wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) { PrusaMenu* menu = dynamic_cast(menu_); + + const wxString menu_name = _(L("Add settings")); // Delete old items from settings popupmenu - auto settings_id = menu->FindItem(_("Add settings")); + auto settings_id = menu->FindItem(menu_name); if (settings_id != wxNOT_FOUND) menu->Destroy(settings_id); @@ -966,14 +983,10 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) menu->m_separator_scnd = menu->AppendSeparator(); // Add full settings list - auto menu_item = new wxMenuItem(menu, wxID_ANY, _(L("Add settings"))); + auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); menu_item->SetBitmap(m_bmp_cog); -// const auto sel_vol = get_selected_model_volume(); -// if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER) -// menu_item->Enable(false); -// else - menu_item->SetSubMenu(create_settings_popupmenu(menu)); + menu_item->SetSubMenu(create_settings_popupmenu(menu)); return menu->Append(menu_item); } @@ -1018,6 +1031,37 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const menu->AppendSeparator(); } +void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const +{ + const wxString name = _(L("Change extruder")); + // Delete old menu item + const int item_id = menu->FindItem(name); + if (item_id != wxNOT_FOUND) + menu->Destroy(item_id); + + const int extruders_cnt = extruders_count(); + const wxDataViewItem item = GetSelection(); + if (item && extruders_cnt > 1) + { + DynamicPrintConfig& config = get_item_config(item); + + const int initial_extruder = !config.has("extruder") ? 0 : + config.option("extruder")->value; + + wxMenu* extruder_selection_menu = new wxMenu(); + + for (int i = 0; i <= extruders_cnt; i++) + { + const wxString& item_name = i == 0 ? _(L("Default")) : wxString::Format("%d", i); + + append_menu_radio_item(extruder_selection_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { set_extruder_for_selected_items(i); }, menu)->Check(i == initial_extruder); + } + + menu->AppendSubMenu(extruder_selection_menu, name, _(L("Select new extruder for the object/part"))); + } +} + void ObjectList::create_object_popupmenu(wxMenu *menu) { #ifdef __WXOSX__ @@ -2425,15 +2469,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const for (const wxDataViewItem& item : sels) { - const ItemType type = m_objects_model->GetItemType(item); - - const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : - m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); - - const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; - - DynamicPrintConfig& config = type & itObject ? (*m_objects)[obj_idx]->config : - (*m_objects)[obj_idx]->volumes[vol_idx]->config; + DynamicPrintConfig& config = get_item_config(item); if (config.has("extruder")) { if (extruder == 0) @@ -2446,7 +2482,16 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const const wxString extruder_str = extruder == 0 ? wxString ("default") : wxString::Format("%d", config.option("extruder")->value); - m_objects_model->SetValue(extruder_str, item, 1); + + auto const type = m_objects_model->GetItemType(item); + + /* We can change extruder for Object/Volume only. + * So, if Instance is selected, get its Object item and change it + */ + m_objects_model->SetValue(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item, 1); + + const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : + m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); wxGetApp().plater()->canvas3D()->ensure_on_bed(obj_idx); } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index e0df8dd40..4ddc6ea71 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -187,6 +187,7 @@ public: void append_menu_items_osx(wxMenu* menu); void append_menu_item_fix_through_netfabb(wxMenu* menu); void append_menu_item_export_stl(wxMenu* menu) const ; + void append_menu_item_change_extruder(wxMenu* menu) const; void create_object_popupmenu(wxMenu *menu); void create_sla_object_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu); @@ -213,12 +214,13 @@ public: wxPoint get_mouse_position_in_control(); wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; + DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const; bool is_parts_changed() const { return m_parts_changed; } bool is_part_settings_changed() const { return m_part_settings_changed; } void part_settings_changed(); - void parts_changed(int obj_idx); - void part_selection_changed(); + void parts_changed(int obj_idx); + void part_selection_changed(); // Add object to the list void add_object_to_list(size_t obj_idx); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6db5a5d5b..c92c22c50 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2624,29 +2624,35 @@ void Plater::priv::on_right_click(Vec2dEvent& evt) sidebar->obj_list()->append_menu_item_settings(menu); - /* Remove/Prepend "increase/decrease instances" menu items according to the view mode. - * Suppress to show those items for a Simple mode - */ - const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF; - if (wxGetApp().get_mode() == comSimple) { - if (menu->FindItem(_(L("Increase copies"))) != wxNOT_FOUND) - { - /* Detach an items from the menu, but don't delete them - * so that they can be added back later - * (after switching to the Advanced/Expert mode) - */ - menu->Remove(items_increase[id]); - menu->Remove(items_decrease[id]); - menu->Remove(items_set_number_of_copies[id]); + if (printer_technology != ptSLA) + sidebar->obj_list()->append_menu_item_change_extruder(menu); + + if (menu != &part_menu) + { + /* Remove/Prepend "increase/decrease instances" menu items according to the view mode. + * Suppress to show those items for a Simple mode + */ + const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF; + if (wxGetApp().get_mode() == comSimple) { + if (menu->FindItem(_(L("Increase copies"))) != wxNOT_FOUND) + { + /* Detach an items from the menu, but don't delete them + * so that they can be added back later + * (after switching to the Advanced/Expert mode) + */ + menu->Remove(items_increase[id]); + menu->Remove(items_decrease[id]); + menu->Remove(items_set_number_of_copies[id]); + } } - } - else { - if (menu->FindItem(_(L("Increase copies"))) == wxNOT_FOUND) - { - // Prepend items to the menu, if those aren't not there - menu->Prepend(items_set_number_of_copies[id]); - menu->Prepend(items_decrease[id]); - menu->Prepend(items_increase[id]); + else { + if (menu->FindItem(_(L("Increase copies"))) == wxNOT_FOUND) + { + // Prepend items to the menu, if those aren't not there + menu->Prepend(items_set_number_of_copies[id]); + menu->Prepend(items_decrease[id]); + menu->Prepend(items_increase[id]); + } } } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index dee18ad43..55544f28e 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -61,6 +61,24 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin return item; } +wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description, + std::function cb, wxEvtHandler* event_handler) +{ + if (id == wxID_ANY) + id = wxNewId(); + + wxMenuItem* item = menu->AppendRadioItem(id, string, description); + +#ifdef __WXMSW__ + if (event_handler != nullptr && event_handler != menu) + event_handler->Bind(wxEVT_MENU, cb, id); + else +#endif // __WXMSW__ + menu->Bind(wxEVT_MENU, cb, id); + + return item; +} + const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200; const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200; const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index a15bee256..27da67deb 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -25,7 +25,11 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr); -wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, const std::string& icon = ""); +wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, + const std::string& icon = ""); + +wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description, + std::function cb, wxEvtHandler* event_handler); wxBitmap create_scaled_bitmap(const std::string& bmp_name); From 1e47cc9004b26aff95ebec7b107db2e973fc1312 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 28 Mar 2019 16:19:30 +0100 Subject: [PATCH 3/4] Improving Zipper API error handling. --- src/libslic3r/PrintExport.hpp | 18 ++++++++++++------ src/libslic3r/SLAPrint.hpp | 14 +++++++++----- src/libslic3r/Zipper.cpp | 35 +++++++++++++++++++++++++++-------- src/libslic3r/Zipper.hpp | 7 ++++++- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index 1b5efcea1..cdb2cc008 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -80,9 +80,8 @@ public: // Provokes static_assert in the right way. template struct VeryFalse { static const bool value = false; }; -// This has to be explicitly implemented in the gui layer or a default zlib -// based implementation is needed. I don't have time for that and I'm delegating -// the implementation to the gui layer where the gui toolkit can cover this. +// This can be explicitly implemented in the gui layer or the default Zipper +// API in libslic3r with minz. template class LayerWriter { public: @@ -91,21 +90,28 @@ public: "No layer writer implementation provided!"); } + // Should create a new file within the zip with the given filename. It + // should also finish any previous entry. void next_entry(const std::string& /*fname*/) {} - // binary entry + // Should create a new file within the archive and write the provided data. void binary_entry(const std::string& /*fname*/, const std::uint8_t* buf, size_t len); + // Get the name of the archive but only the name part without the path or + // the extension. std::string get_name() { return ""; } + // Test whether the object can still be used for writing. bool is_ok() { return false; } + // Write some data (text) into the current file (entry) within the archive. template LayerWriter& operator<<(T&& /*arg*/) { return *this; } - void close() {} + // Flush the current entry into the archive. + void finalize() {} }; // Implementation for PNG raster output @@ -267,7 +273,7 @@ public: } } - writer.close(); + writer.finalize(); } catch(std::exception& e) { BOOST_LOG_TRIVIAL(error) << e.what(); // Rethrow the exception diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index dfaa2bff5..54302cf0d 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -208,18 +208,18 @@ template<> class LayerWriter { Zipper m_zip; public: - inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} + LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} - inline void next_entry(const std::string& fname) { m_zip.add_entry(fname); } + void next_entry(const std::string& fname) { m_zip.add_entry(fname); } - inline void binary_entry(const std::string& fname, + void binary_entry(const std::string& fname, const std::uint8_t* buf, size_t l) { m_zip.add_entry(fname, buf, l); } - inline std::string get_name() const { + std::string get_name() const { return m_zip.get_name(); } @@ -231,7 +231,11 @@ public: return true; // m_zip blows up if something goes wrong... } - inline void close() { m_zip.close(); } + // After finalize, no writing to the archive will have an effect. The only + // valid operation is to dispose the object calling the destructor which + // should close the file. This method can throw and signal potential errors + // when flushing the archive. This is why its present. + void finalize() { m_zip.finalize(); } }; /** diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index 490805c0d..6b7faaddc 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -111,6 +111,11 @@ public: { throw std::runtime_error(formatted_errorstr()); } + + bool is_alive() + { + return arch.m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + } }; Zipper::Zipper(const std::string &zipfname, e_compression compression) @@ -129,11 +134,19 @@ Zipper::Zipper(const std::string &zipfname, e_compression compression) Zipper::~Zipper() { - try { - close(); - } catch(...) { - BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr(); + if(m_impl->is_alive()) { + // Flush the current entry if not finished yet. + try { finish_entry(); } catch(...) { + BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr(); + } + + if(!mz_zip_writer_finalize_archive(&m_impl->arch)) + BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr(); } + + // The file should be closed no matter what... + if(!mz_zip_writer_end(&m_impl->arch)) + BOOST_LOG_TRIVIAL(error) << m_impl->formatted_errorstr(); } Zipper::Zipper(Zipper &&m): @@ -152,12 +165,16 @@ Zipper &Zipper::operator=(Zipper &&m) { void Zipper::add_entry(const std::string &name) { + if(!m_impl->is_alive()) return; + finish_entry(); // finish previous business m_entry = name; } void Zipper::add_entry(const std::string &name, const uint8_t *data, size_t l) { + if(!m_impl->is_alive()) return; + finish_entry(); mz_uint cmpr = MZ_NO_COMPRESSION; switch (m_compression) { @@ -175,7 +192,9 @@ void Zipper::add_entry(const std::string &name, const uint8_t *data, size_t l) void Zipper::finish_entry() { - if(!m_data.empty() > 0 && !m_entry.empty()) { + if(!m_impl->is_alive()) return; + + if(!m_data.empty() && !m_entry.empty()) { mz_uint compression = MZ_NO_COMPRESSION; switch (m_compression) { @@ -198,12 +217,12 @@ std::string Zipper::get_name() const { return boost::filesystem::path(m_impl->m_zipname).stem().string(); } -void Zipper::close() +void Zipper::finalize() { finish_entry(); - if(!mz_zip_writer_finalize_archive(&m_impl->arch)) m_impl->blow_up(); - if(!mz_zip_writer_end(&m_impl->arch)) m_impl->blow_up(); + if(m_impl->is_alive()) if(!mz_zip_writer_finalize_archive(&m_impl->arch)) + m_impl->blow_up(); } } diff --git a/src/libslic3r/Zipper.hpp b/src/libslic3r/Zipper.hpp index 7319c4ac4..6566dad42 100644 --- a/src/libslic3r/Zipper.hpp +++ b/src/libslic3r/Zipper.hpp @@ -47,6 +47,7 @@ public: void add_entry(const std::string& name); /// Add a new binary file entry with an instantly given byte buffer. + /// This method throws exactly like finish_entry() does. void add_entry(const std::string& name, const std::uint8_t* data, size_t l); // Writing data to the archive works like with standard streams. The target @@ -74,12 +75,16 @@ public: /// buffer and ones an entry is added, the buffer will bind to the new entry /// If the buffer was written, but no entry was added, the buffer will be /// cleared after this call. + /// + /// This method will throw a runtime exception if an error occures. The + /// entry will still be open (with the data intact) but the state of the + /// file is up to minz after the erroneous write. void finish_entry(); /// Gets the name of the archive without the path or extension. std::string get_name() const; - void close(); + void finalize(); }; From 49fe8a9f0892111447a2a78d1bb1e327c34e9a8f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 29 Mar 2019 10:48:20 +0100 Subject: [PATCH 4/4] Volumes selected from the sidebar table taken from the currently selected instance --- src/slic3r/GUI/GUI_ObjectList.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 093d77814..224bb802e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2033,7 +2033,7 @@ void ObjectList::update_selections_on_canvas() return; } - auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, bool as_single_selection) + auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection) { if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection); @@ -2043,7 +2043,7 @@ void ObjectList::update_selections_on_canvas() if (m_objects_model->GetItemType(item) == itVolume) { const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); const int vol_idx = m_objects_model->GetVolumeIdByItem(item); - selection.add_volume(obj_idx, vol_idx, 0, as_single_selection); + selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection); } else if (m_objects_model->GetItemType(item) == itInstance) { const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); @@ -2055,10 +2055,10 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) - add_to_selection(m_objects_model->GetParent(item), selection, true); + add_to_selection(m_objects_model->GetParent(item), selection, -1, true); else - add_to_selection(item, selection, true); - + add_to_selection(item, selection, -1, true); + wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state(); return; } @@ -2066,9 +2066,11 @@ void ObjectList::update_selections_on_canvas() wxDataViewItemArray sels; GetSelections(sels); + // stores current instance idx before to clear the selection + int instance_idx = selection.get_instance_idx(); selection.clear(); for (auto item: sels) - add_to_selection(item, selection, false); + add_to_selection(item, selection, instance_idx, false); wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state(); }