diff --git a/resources/icons/info.png b/resources/icons/info.png new file mode 100644 index 000000000..9eeee9b3c Binary files /dev/null and b/resources/icons/info.png differ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 58560c8bd..dbfc05a6d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1543,6 +1543,7 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item)); m_objects_model->Delete(item); + update_info_items(obj_idx); } void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) @@ -2118,20 +2119,32 @@ void ObjectList::part_selection_changed() { if (item) { - if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) { - obj_idx = m_objects_model->GetIdByItem(item); + const ItemType type = m_objects_model->GetItemType(item); + const wxDataViewItem parent = m_objects_model->GetParent(item); + const ItemType parent_type = m_objects_model->GetItemType(parent); + obj_idx = m_objects_model->GetObjectIdByItem(item); + + if (parent == wxDataViewItem(nullptr) + || type == itInfo) { og_name = _(L("Object manipulation")); m_config = &(*m_objects)[obj_idx]->config; update_and_show_manipulations = true; + + if (type == itInfo) { + Unselect(item); + assert(parent_type == itObject); + Select(parent); + InfoItemType info_type = m_objects_model->GetInfoItemType(item); + GLGizmosManager::EType gizmo_type = + info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports + : GLGizmosManager::EType::Seam; + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) + gizmos_mgr.open_gizmo(gizmo_type); + } } else { - obj_idx = m_objects_model->GetObjectIdByItem(item); - - const ItemType type = m_objects_model->GetItemType(item); if (type & itSettings) { - const auto parent = m_objects_model->GetParent(item); - const ItemType parent_type = m_objects_model->GetItemType(parent); - if (parent_type & itObject) { og_name = _(L("Object Settings to modify")); m_config = &(*m_objects)[obj_idx]->config; @@ -2243,6 +2256,38 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D return ret; } + +void ObjectList::update_info_items(size_t obj_idx) +{ + const ModelObject* model_object = (*m_objects)[obj_idx]; + wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); + assert(item_obj.IsOk()); + + for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam}) { + wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); + bool shows = item.IsOk(); + bool should_show = printer_technology() == ptFFF + && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), + [type](const ModelVolume* mv) { + return ! (type == InfoItemType::CustomSupports + ? mv->supported_facets.empty() + : mv->seam_facets.empty()); + }); + + if (! shows && should_show) { + m_objects_model->AddInfoChild(item_obj, type); + Expand(item_obj); + } + else if (shows && ! should_show) { + Unselect(item); + m_objects_model->Delete(item); + Select(item_obj); + } + } +} + + + void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; @@ -2251,6 +2296,8 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) model_object->config.has("extruder") ? model_object->config.extruder() : 0, get_mesh_errors_count(obj_idx) > 0); + update_info_items(obj_idx); + // add volumes to the object if (model_object->volumes.size() > 1) { for (const ModelVolume* volume : model_object->volumes) { @@ -3029,7 +3076,7 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); - if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) + if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else add_to_selection(item, selection, instance_idx, mode); @@ -3442,6 +3489,9 @@ void ObjectList::update_object_list_by_printer_technology() m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); for (auto& object_item : object_items) { + // update custom supports info + update_info_items(m_objects_model->GetObjectIdByItem(object_item)); + // Update Settings Item for object update_settings_item_and_selection(object_item, sel); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index d70c53849..5812e26f7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -18,7 +18,6 @@ class wxBoxSizer; class wxBitmapComboBox; class wxMenuItem; -class ObjectDataViewModel; class MenuWithSeparators; namespace Slic3r { @@ -347,6 +346,7 @@ public: void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); + void update_info_items(size_t obj_idx); void instances_to_separated_object(const int obj_idx, const std::set& inst_idx); void instances_to_separated_objects(const int obj_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index f3f87cc33..d87068712 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -316,8 +317,12 @@ void GLGizmoFdmSupports::update_model_object() const updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 6c3a8ddd3..972476755 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -7,7 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" - +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -222,8 +222,12 @@ void GLGizmoSeam::update_model_object() const updated |= mv->seam_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 453b98dad..b1f38d309 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -165,6 +165,17 @@ void GLGizmosManager::reset_all_states() m_hover = Undefined; } +bool GLGizmosManager::open_gizmo(EType type) +{ + int idx = int(type); + if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) { + activate_gizmo(m_current == idx ? Undefined : (EType)idx); + update_data(); + return true; + } + return false; +} + void GLGizmosManager::set_hover_id(int id) { if (!m_enabled || m_current == Undefined) @@ -268,24 +279,21 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled) + if (!m_enabled || m_parent.get_selection().is_empty()) return false; - if (m_parent.get_selection().is_empty()) + auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(), + [key](const std::unique_ptr& gizmo) { + int gizmo_key = gizmo->get_shortcut_key(); + return gizmo->is_selectable() + && ((gizmo_key == key - 64) || (gizmo_key == key - 96)); + }); + + if (it == m_gizmos.end()) return false; - bool handled = false; - - for (size_t idx : get_selectable_idxs()) { - int it_key = m_gizmos[idx]->get_shortcut_key(); - - if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { - activate_gizmo(m_current == idx ? Undefined : (EType)idx); - handled = true; - } - } - - return handled; + EType gizmo_type = EType(it - m_gizmos.begin()); + return open_gizmo(gizmo_type); } bool GLGizmosManager::is_dragging() const @@ -819,10 +827,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) - { - update_data(); processed = true; - } } if (processed) @@ -1161,5 +1166,11 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const return true; } + +int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const +{ + return m_gizmos[type]->get_shortcut_key(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 02fcc4ed8..01d7ea85c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -172,6 +172,7 @@ public: void refresh_on_off_state(); void reset_all_states(); bool is_serializing() const { return m_serializing; } + bool open_gizmo(EType type); void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); @@ -229,6 +230,7 @@ public: void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); } + int get_shortcut_key(GLGizmosManager::EType) const; private: void render_background(float left, float top, float right, float bottom, float border) const; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index cf7b7b479..395e329e8 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -47,6 +47,19 @@ void ObjectDataViewModelNode::init_container() static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; +static constexpr char InfoIcon[] = "info"; + +ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) : + m_parent(parent), + m_type(itInfo), + m_extruder(wxEmptyString) +{ + m_name = info_type == InfoItemType::CustomSupports + ? _L("Paint-on supports") + : _L("Paint-on seam"); + m_info_item_type = info_type; +} + ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), @@ -69,6 +82,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr m_name = _(L("Layers")); } + else if (type == itInfo) + assert(false); if (type & (itInstanceRoot | itLayerRoot)) init_container(); @@ -250,6 +265,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); + m_info_bmp = create_scaled_bitmap(InfoIcon); } ObjectDataViewModel::~ObjectDataViewModel() @@ -330,13 +346,44 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent return child; } +wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type) +{ + ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); + if (!root) return wxDataViewItem(0); + + const auto node = new ObjectDataViewModelNode(root, info_type); + + // The new item should be added according to its order in InfoItemType. + // Find last info item with lower index and append after it. + const auto& children = root->GetChildren(); + int idx = -1; + for (int i=0; iGetType() == itInfo && int(children[i]->GetInfoItemType()) < int(info_type) ) + idx = i; + } + + root->Insert(node, idx+1); + node->SetBitmap(m_info_bmp); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) { ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); if (!root) return wxDataViewItem(0); const auto node = new ObjectDataViewModelNode(root, itSettings); - root->Insert(node, 0); + + // In case there are some info items, append after them. + size_t i = 0; + for (i = 0; iGetChildCount(); ++i) + if (root->GetNthChild(i)->GetType() != itInfo) + break; + + root->Insert(node, i); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); @@ -1379,6 +1426,14 @@ ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const return node->m_type < 0 ? itUndef : node->m_type; } +InfoItemType ObjectDataViewModel::GetInfoItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return InfoItemType::Undef; + ObjectDataViewModelNode *node = static_cast(item.GetID()); + return node->m_info_item_type; +} + wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const { if (!parent_item.IsOk()) @@ -1411,6 +1466,21 @@ wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) return GetItemByType(item, itLayerRoot); } +wxDataViewItem ObjectDataViewModel::GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const +{ + if (! parent_item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = static_cast(parent_item.GetID()); + for (size_t i = 0; i < node->GetChildCount(); i++) { + const ObjectDataViewModelNode* child_node = node->GetNthChild(i); + if (child_node->m_type == itInfo && child_node->m_info_item_type == type) + return wxDataViewItem((void*)child_node); + } + + return wxDataViewItem(0); // not found +} + bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const { if (!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c23ec195b..cf440f5dc 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -27,6 +27,7 @@ enum ItemType { itSettings = 16, itLayerRoot = 32, itLayer = 64, + itInfo = 128 }; enum ColumnNumber @@ -44,6 +45,13 @@ enum PrintIndicator piUnprintable , // unprintable }; +enum class InfoItemType +{ + Undef, + CustomSupports, + CustomSeam +}; + class ObjectDataViewModelNode; WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); @@ -69,6 +77,7 @@ class ObjectDataViewModelNode std::string m_action_icon_name = ""; ModelVolumeType m_volume_type; + InfoItemType m_info_item_type {InfoItemType::Undef}; public: ObjectDataViewModelNode(const wxString& name, @@ -104,6 +113,7 @@ public: const wxString& extruder = wxEmptyString ); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType type); ~ObjectDataViewModelNode() { @@ -176,6 +186,7 @@ public: const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } + InfoItemType GetInfoItemType() const { return m_info_item_type; } void SetIdx(const int& idx); int GetIdx() const { return m_idx; } ModelVolumeType GetVolumeType() { return m_volume_type; } @@ -244,6 +255,7 @@ class ObjectDataViewModel :public wxDataViewModel std::vector m_objects; std::vector m_volume_bmps; wxBitmap m_warning_bmp; + wxBitmap m_info_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -261,6 +273,7 @@ public: const int extruder = 0, const bool create_frst_child = true); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector& print_indicator); wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); @@ -335,12 +348,15 @@ public: // In our case it is an item with all columns bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - ItemType GetItemType(const wxDataViewItem &item) const ; + ItemType GetItemType(const wxDataViewItem &item) const; + InfoItemType GetInfoItemType(const wxDataViewItem &item) const; wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, ItemType type) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; + wxDataViewItem GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const; + bool IsSettingsItem(const wxDataViewItem &item) const; void UpdateSettingsDigest( const wxDataViewItem &item, const std::vector& categories);