From 81809018bc9087cb563fc74591eac4a7309a91f6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 2 Apr 2019 16:33:52 +0200 Subject: [PATCH] Selection improvements + "Delete" menu_item for Instances under OSX --- src/slic3r/GUI/GUI_ObjectList.cpp | 158 ++++++++++++++++++++++-------- src/slic3r/GUI/GUI_ObjectList.hpp | 10 ++ 2 files changed, 129 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4c46e7135..b61b8280f 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -82,13 +82,14 @@ ObjectList::ObjectList(wxWindow* parent) : init_icons(); // describe control behavior - Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { + Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) { #ifndef __APPLE__ // On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called // before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object // manipulator cache with the following call to selection_changed() wxGetApp().obj_manipul()->emulate_kill_focus(); #endif // __APPLE__ + m_last_selected_item = event.GetItem(); selection_changed(); #ifndef __WXMSW__ set_tooltip_for_item(get_mouse_position_in_control()); @@ -1006,8 +1007,7 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu) void ObjectList::append_menu_items_osx(wxMenu* menu) { - append_menu_item(menu, wxID_ANY, _(L("Delete item")), "", - [this](wxCommandEvent&) { remove(); }, "", menu); + append_menu_item_delete(menu); append_menu_item(menu, wxID_ANY, _(L("Rename")), "", [this](wxCommandEvent&) { rename_item(); }, "", menu); @@ -1062,6 +1062,12 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const } } +void ObjectList::append_menu_item_delete(wxMenu* menu) +{ + append_menu_item(menu, wxID_ANY, _(L("Delete")), "", + [this](wxCommandEvent&) { remove(); }, "", menu); +} + void ObjectList::create_object_popupmenu(wxMenu *menu) { #ifdef __WXOSX__ @@ -1118,6 +1124,9 @@ void ObjectList::create_part_popupmenu(wxMenu *menu) void ObjectList::create_instance_popupmenu(wxMenu*menu) { +#ifdef __WXOSX__ + append_menu_item_delete(menu); +#endif // __WXOSX__ m_menu_item_split_instances = append_menu_item_instance_to_object(menu); wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { @@ -1552,7 +1561,7 @@ void ObjectList::split() auto opt_keys = model_object->volumes[id]->config.keys(); if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { select_item(m_objects_model->AddSettingsChild(vol_item)); - Collapse(vol_item); + /*Collapse*/Expand(vol_item); } } @@ -1746,7 +1755,7 @@ void ObjectList::add_object_to_list(size_t obj_idx) auto opt_keys = model_object->volumes[id]->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { select_item(m_objects_model->AddSettingsChild(vol_item)); - Collapse(vol_item); + /*Collapse*/Expand(vol_item); } } Expand(item); @@ -1760,7 +1769,7 @@ void ObjectList::add_object_to_list(size_t obj_idx) auto opt_keys = model_object->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { select_item(m_objects_model->AddSettingsChild(item)); - Collapse(item); + /*Collapse*/Expand(item); } #ifndef __WXOSX__ @@ -1940,6 +1949,8 @@ void ObjectList::update_selections() const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; + m_selection_mode = smInstance; + // We doesn't update selection if SettingsItem for the current object/part is selected if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) { @@ -1953,24 +1964,6 @@ void ObjectList::update_selections() return; } } -// if (selection.is_single_full_object() && selection.get_instance_idx() != -1) -// { -// sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); -// } -// else if (selection.is_single_volume() || selection.is_modifier() || -// selection.is_multiple_volume() || selection.is_multiple_full_object()) -// { -// for (auto idx : selection.get_volume_idxs()) { -// const auto gl_vol = selection.get_volume(idx); -// if (selection.is_multiple_full_object()) -// sels.Add(m_objects_model->GetItemById(gl_vol->object_idx())); -// else if (gl_vol->volume_idx() >= 0) -// // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids -// // are not associated with ModelVolumes, but they are temporarily generated by the backend -// // (for example, SLA supports or SLA pad). -// sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); -// } -// } else if (selection.is_single_full_object() || selection.is_multiple_full_object()) { const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content(); @@ -1985,8 +1978,10 @@ void ObjectList::update_selections() bool root_is_selected = false; for (const auto& item:current_sels) - if (item == m_objects_model->GetParent(frst_inst_item)) { + if (item == m_objects_model->GetParent(frst_inst_item) || + item == m_objects_model->GetTopParent(frst_inst_item)) { root_is_selected = true; + sels.Add(item); break; } if (root_is_selected) @@ -2008,6 +2003,7 @@ void ObjectList::update_selections() // (for example, SLA supports or SLA pad). sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); } + m_selection_mode = smVolume; } else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) { @@ -2017,7 +2013,7 @@ void ObjectList::update_selections() } else if (selection.is_mixed()) { - auto& objects_content_list = selection.get_content(); + const Selection::ObjectIdxsToInstanceIdxsMap& objects_content_list = selection.get_content(); for (auto idx : selection.get_volume_idxs()) { const auto gl_vol = selection.get_volume(idx); @@ -2050,6 +2046,9 @@ void ObjectList::update_selections() sels.Add(m_objects_model->GetItemByVolumeId(glv_obj_idx, glv_vol_idx)); } } + + if (sels.size() == 0) + m_selection_mode = smUndef; select_items(sels); @@ -2085,29 +2084,34 @@ void ObjectList::update_selections_on_canvas() 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); + const ItemType& type = m_objects_model->GetItemType(item); + if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) { + wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item; + selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection); return; } - if (m_objects_model->GetItemType(item) == itVolume) { + if (type == 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, std::max(instance_idx, 0), as_single_selection); } - else if (m_objects_model->GetItemType(item) == itInstance) { + else if (type == itInstance) { const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); const int inst_idx = m_objects_model->GetInstanceIdByItem(item); selection.add_instance(obj_idx, inst_idx, as_single_selection); } }; + // stores current instance idx before to clear the selection + int instance_idx = selection.get_instance_idx(); + if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) - add_to_selection(m_objects_model->GetParent(item), selection, -1, true); + add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true); else - add_to_selection(item, selection, -1, true); + add_to_selection(item, selection, instance_idx, true); wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state(); return; @@ -2116,8 +2120,6 @@ 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, instance_idx, false); @@ -2163,22 +2165,79 @@ void ObjectList::select_item_all_children() if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { for (int i = 0; i < m_objects->size(); i++) sels.Add(m_objects_model->GetItemById(i)); + m_selection_mode = smInstance; } else { const auto item = GetSelection(); // Some volume(instance) is selected => select all volumes(instances) inside the current object - if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) { + if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) m_objects_model->GetChildren(m_objects_model->GetParent(item), sels); - } + + m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance; } SetSelections(sels); selection_changed(); } +// update selection mode for non-multiple selection +void ObjectList::update_selection_mode() +{ + m_last_selected_item = wxDataViewItem(0); + + // All items are unselected + if (!GetSelection()) + { + m_selection_mode = smUndef; + return; + } + + const ItemType type = m_objects_model->GetItemType(GetSelection()); + m_selection_mode = type&itSettings ? smUndef : + type&itVolume ? smVolume : smInstance; +} + +// check last selected item. If is it possible to select it +bool ObjectList::check_last_selection() +{ + if (!m_last_selected_item) + return true; + + /* We can't mix Parts and Objects/Instances. + * So, unselect last selected item and show information about it + */ + const ItemType type = m_objects_model->GetItemType(m_last_selected_item); + if (type & itSettings || + type & itVolume && m_selection_mode == smInstance || + !(type & itVolume) && m_selection_mode == smVolume) + { + Unselect(m_last_selected_item); + + // Inform user why selection isn't complited + const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part")); + + const wxString msg_str = wxString::Format(_(L("You started your selection with %s Item.")) + "\n" + + _(L("In this mode you can select only another %s Items%s")), item_type, item_type, + m_selection_mode == smInstance ? "." : " " + _(L("of a current Object"))); + + wxMessageDialog dialog(this, _(L("Unsupported selection")) + "\n\n" + msg_str, + _(L("Info")), wxICON_INFORMATION); + dialog.ShowModal(); + + return false; + } + + return true; +} + void ObjectList::fix_multiselection_conflicts() { - if (GetSelectedItemsCount() <= 1) + if (GetSelectedItemsCount() <= 1) { + update_selection_mode(); + return; + } + + if (!check_last_selection()) return; m_prevent_list_events = true; @@ -2186,6 +2245,8 @@ void ObjectList::fix_multiselection_conflicts() wxDataViewItemArray sels; GetSelections(sels); + m_selection_mode = smInstance; + for (const auto item : sels) { if (!IsSelected(item)) // if this item is unselected now (from previous actions) continue; @@ -2205,6 +2266,26 @@ void ObjectList::fix_multiselection_conflicts() for (const auto unsel_item : unsels) Unselect(unsel_item); } + + if (m_objects_model->GetItemType(item) == itVolume) + { + // If some part is selected, unselect all items except of selected parts of the current object + sels.clear(); + + wxDataViewItemArray children; // selected volumes from current parent + m_objects_model->GetChildren(parent, children); + for (const auto child : children) + { + if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume) + sels.Add(child); + } + + UnselectAll(); + SetSelections(sels); + + m_selection_mode = smVolume; + break; + } } m_prevent_list_events = false; @@ -2489,8 +2570,7 @@ void ObjectList::show_multi_selection_menu() wxMenu* menu = new wxMenu(); #ifdef __WXOSX__ - append_menu_item(menu, wxID_ANY, _(L("Delete items")), "", - [this](wxCommandEvent&) { remove(); }, "", menu); + append_menu_item_delete(); #endif //__WXOSX__ if (extruders_count() > 1) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b0ed756f8..0611cc4f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -60,6 +60,12 @@ struct ItemForDelete class ObjectList : public wxDataViewCtrl { + enum SELECTION_MODE + { + smUndef, + smVolume, + smInstance + } m_selection_mode {smUndef}; struct dragged_item_data { @@ -135,6 +141,7 @@ class ObjectList : public wxDataViewCtrl bool m_part_settings_changed = false; int m_selected_row = 0; + wxDataViewItem m_last_selected_item {nullptr}; #if 0 FreqSettingsBundle m_freq_settings_fff; @@ -188,6 +195,7 @@ public: 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 append_menu_item_delete(wxMenu* menu); void create_object_popupmenu(wxMenu *menu); void create_sla_object_popupmenu(wxMenu*menu); void create_part_popupmenu(wxMenu*menu); @@ -258,6 +266,8 @@ public: void select_items(const wxDataViewItemArray& sels); void select_all(); void select_item_all_children(); + void update_selection_mode(); + bool check_last_selection(); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts();