diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3426fd02a4..2d96c35e77 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -97,7 +97,7 @@ void ObjectList::create_objects_ctrl() // temporary workaround for the correct behavior of the Scrolled sidebar panel: // 1. set a height of the list to some big value // 2. change it to the normal min value (200) after first whole App updating/layouting - SetMinSize(wxSize(-1, 1500)); // #ys_FIXME + SetMinSize(wxSize(-1, 3000)); // #ys_FIXME m_sizer = new wxBoxSizer(wxVERTICAL); m_sizer->Add(this, 1, wxGROW | wxLEFT, 20); @@ -445,13 +445,21 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) // only allow drags for item, not containers if (multiple_selection() || GetSelection()!=item || - m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume ) { + m_objects_model->GetParent(item) == wxDataViewItem(0)) { + event.Veto(); + return; + } + + const ItemType& type = m_objects_model->GetItemType(item); + if (!(type & (itVolume | itInstance))) { event.Veto(); return; } - m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_objects_model->GetVolumeIdByItem(item)); + m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), + type&itVolume ? m_objects_model->GetVolumeIdByItem(item) : + m_objects_model->GetInstanceIdByItem(item), + type); /* Under MSW or OSX, DnD moves an item to the place of another selected item * But under GTK, DnD moves an item between another two items. @@ -470,31 +478,41 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move; } +bool ObjectList::can_drop(const wxDataViewItem& item) const +{ + return m_dragged_data.type() == itInstance && !item.IsOk() || + m_dragged_data.type() == itVolume && item.IsOk() && + m_objects_model->GetItemType(item) == itVolume && + m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item); +} + void ObjectList::OnDropPossible(wxDataViewEvent &event) { - wxDataViewItem item(event.GetItem()); + const wxDataViewItem& item = event.GetItem(); - // only allow drags for item or background, not containers - if (!item.IsOk() || - m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) + if (!can_drop(item)) event.Veto(); } void ObjectList::OnDrop(wxDataViewEvent &event) { - wxDataViewItem item(event.GetItem()); + const wxDataViewItem& item = event.GetItem(); - if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) || - m_objects_model->GetItemType(item) != itVolume || - m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) { + if (!can_drop(item)) + { event.Veto(); m_dragged_data.clear(); return; } - const int from_volume_id = m_dragged_data.vol_idx(); + if (m_dragged_data.type() == itInstance) + { + instance_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.sub_obj_idx()); + m_dragged_data.clear(); + return; + } + + const int from_volume_id = m_dragged_data.sub_obj_idx(); int to_volume_id = m_objects_model->GetVolumeIdByItem(item); // It looks like a fixed in current version of the wxWidgets @@ -506,7 +524,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event) // if (to_volume_id > from_volume_id) to_volume_id--; // #endif // __WXGTK__ - auto& volumes = (*m_objects)[/*m_selected_object_id*/m_dragged_data.obj_idx()]->volumes; + auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; auto delta = to_volume_id < from_volume_id ? -1 : 1; int cnt = 0; for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) @@ -516,7 +534,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event) m_objects_model->GetParent(item))); m_parts_changed = true; - parts_changed(/*m_selected_object_id*/m_dragged_data.obj_idx()); + parts_changed(m_dragged_data.obj_idx()); m_dragged_data.clear(); } @@ -1729,6 +1747,25 @@ void ObjectList::update_settings_items() UnselectAll(); } +void ObjectList::instance_to_separated_object(const int obj_idx, const int inst_idx) +{ + // create new object from selected instance + ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]); + for (int i = model_object->instances.size() - 1; i >= 0; i--) + { + if (i == inst_idx) + continue; + model_object->delete_instance(i); + } + + // Add new object to the object_list + add_object_to_list(m_objects->size() - 1); + + // delete selected instance from the object + del_subobject_from_object(obj_idx, inst_idx, itInstance); + delete_instance_from_list(obj_idx, inst_idx); +} + void ObjectList::ItemValueChanged(wxDataViewEvent &event) { if (event.GetColumn() == 0) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 7631782dfc..21db30ad53 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -56,22 +56,27 @@ class ObjectList : public wxDataViewCtrl struct dragged_item_data { - void init(const int obj_idx, const int vol_idx) { + void init(const int obj_idx, const int subobj_idx, const ItemType type) { m_obj_idx = obj_idx; - m_vol_idx = vol_idx; + m_subobj_idx = subobj_idx; + m_type = type; } void clear() { m_obj_idx = -1; - m_vol_idx = -1; + m_subobj_idx = -1; + m_type = itUndef; } int obj_idx() const { return m_obj_idx; } - int vol_idx() const { return m_vol_idx; } + int sub_obj_idx() const { return m_subobj_idx; } + ItemType type() const { return m_type; } private: int m_obj_idx = -1; - int m_vol_idx = -1; + int m_subobj_idx = -1; + ItemType m_type = itUndef; + } m_dragged_data; wxBoxSizer *m_sizer {nullptr}; @@ -222,6 +227,8 @@ public: bool has_multi_part_objects(); void update_settings_items(); + void instance_to_separated_object(const int obj_idx, const int inst_idx); + private: void OnChar(wxKeyEvent& event); void OnContextMenu(wxDataViewEvent &event); @@ -229,6 +236,7 @@ private: void OnBeginDrag(wxDataViewEvent &event); void OnDropPossible(wxDataViewEvent &event); void OnDrop(wxDataViewEvent &event); + bool can_drop(const wxDataViewItem& item) const ; void ItemValueChanged(wxDataViewEvent &event); void OnEditingDone(wxDataViewEvent &event); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index da11044bcc..17f5a2a636 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1063,6 +1063,7 @@ private: bool can_delete_object() const; bool can_increase_instances() const; bool can_decrease_instances() const; + bool can_set_instance_to_object() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; bool can_split() const; @@ -2359,11 +2360,17 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/ [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png"); wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")), [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png"); + + menu->AppendSeparator(); + wxMenuItem* item_instance_to_object = append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")) + dots, _(L("Set an Instance as a Separate Object")), + [this](wxCommandEvent&) { q->instance_to_separated_object(); }, ""); + if (q != nullptr) { q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId()); q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_set_instance_to_object()); }, item_instance_to_object->GetId()); } menu->AppendSeparator(); @@ -2426,9 +2433,9 @@ bool Plater::priv::complit_init_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_volumes->GetId()); } return true; } @@ -2447,7 +2454,7 @@ bool Plater::priv::complit_init_sla_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2466,7 +2473,7 @@ bool Plater::priv::complit_init_part_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId()); } return true; @@ -2534,6 +2541,12 @@ bool Plater::priv::can_increase_instances() const return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); } +bool Plater::priv::can_set_instance_to_object() const +{ + const int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); +} + bool Plater::priv::can_decrease_instances() const { int obj_idx = get_selected_object_idx(); @@ -2760,6 +2773,16 @@ void Plater::set_number_of_copies(/*size_t num*/) decrease_instances(-diff); } +void Plater::instance_to_separated_object() +{ + const int obj_idx = p->get_selected_object_idx(); + const int inst_idx = p->get_selection().get_instance_idx(); + if (obj_idx == -1 || inst_idx == -1) + return; + + sidebar().obj_list()->instance_to_separated_object(obj_idx, inst_idx); +} + bool Plater::is_selection_empty() const { return p->get_selection().is_empty(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7b19d6f313..a0655ed550 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -136,6 +136,7 @@ public: void increase_instances(size_t num = 1); void decrease_instances(size_t num = 1); void set_number_of_copies(/*size_t num*/); + void instance_to_separated_object(); bool is_selection_empty() const; void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);