diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 4a63a82b0..a11e4c188 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -36,11 +36,34 @@ namespace Notifications_Internal{ ImGui::PushStyleColor(idx, col); } } -//ScalableBitmap bmp_icon; + +#if 1 +// Reuse ImGUI Windows. +int NotificationManager::NotificationIDProvider::allocate_id() +{ + int id; + if (m_released_ids.empty()) + id = ++m_next_id; + else { + id = m_released_ids.back(); + m_released_ids.pop_back(); + } + return id; +} +void NotificationManager::NotificationIDProvider::release_id(int id) +{ + m_released_ids.push_back(id); +} +#else +// Don't reuse ImGUI Windows, allocate a new ID every time. +int NotificationManager::NotificationIDProvider::allocate_id() { return ++ m_next_id; } +void NotificationManager::NotificationIDProvider::release_id(int) {} +#endif + //------PopNotification-------- -NotificationManager::PopNotification::PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler) : +NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) : m_data (n) - , m_id (id) + , m_id_provider (id_provider) , m_remaining_time (n.duration) , m_last_remaining_time (n.duration) , m_counting_down (n.duration != 0) @@ -71,7 +94,6 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif Size cnv_size = canvas.get_canvas_size(); ImGuiWrapper& imgui = *wxGetApp().imgui(); bool shown = true; - std::string name; ImVec2 mouse_pos = ImGui::GetMousePos(); float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : (move_from_slope ? slope_width /*+ m_line_height * 0.3f*/ : 0)); @@ -134,8 +156,15 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif } //name of window - probably indentifies window and is shown so last_end add whitespaces according to id - for (size_t i = 0; i < m_id; i++) - name += " "; + if (! m_id) + m_id = m_id_provider.allocate_id(); + std::string name; + { + // Create a unique ImGUI window name. The name may be recycled using a name of an already released notification. + char buf[32]; + sprintf(buf, "!!Ntfctn%d", m_id); + name = buf; + } if (imgui.begin(name, &shown, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar )) { if (shown) { @@ -562,8 +591,8 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } -NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool large) : - NotificationManager::PopNotification(n, id, evt_handler) +NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : + NotificationManager::PopNotification(n, id_provider, evt_handler) { set_large(large); } @@ -654,7 +683,7 @@ void NotificationManager::push_slicing_warning_notification(const std::string& t { NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }; - auto notification = std::make_unique(data, m_next_id++, m_evt_handler); + auto notification = std::make_unique(data, m_id_provider, m_evt_handler); notification->set_object_id(oid); notification->set_warning_step(warning_step); if (push_notification_data(std::move(notification), canvas, 0)) { @@ -730,7 +759,7 @@ void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, time = 0; } NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext }; - push_notification_data(std::make_unique(data, m_next_id++, m_evt_handler, large), + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, large), canvas, timestamp); } void NotificationManager::set_slicing_complete_print_time(std::string info) @@ -778,7 +807,7 @@ void NotificationManager::compare_warning_oids(const std::vector& living } bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp) { - return push_notification_data(std::make_unique(notification_data, m_next_id++, m_evt_handler), canvas, timestamp); + return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), canvas, timestamp); } bool NotificationManager::push_notification_data(std::unique_ptr notification, GLCanvas3D& canvas, int timestamp) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index ecd103051..2b51c720a 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -28,37 +28,33 @@ enum class NotificationType // Notification on end of slicing and G-code processing (the full G-code preview is available). // Contains a hyperlink to export the G-code to a removable media. SlicingComplete, - // Not used. - SlicingNotPossible, +// SlicingNotPossible, // Notification on end of export to a removable media, with hyperling to eject the external media. ExportToRemovableFinished, // Works on OSX only. //FIXME Do we want to have it on Linux and Windows? Is it possible to get the Disconnect event on Windows? Mouse3dDisconnected, - // Not used. - Mouse3dConnected, - // Not used. - NewPresetsAviable, +// Mouse3dConnected, +// NewPresetsAviable, // Notification on the start of PrusaSlicer, when a new PrusaSlicer version is published. // Contains a hyperlink to open a web browser pointing to the PrusaSlicer download location. NewAppAviable, // Notification on the start of PrusaSlicer, when updates of system profiles are detected. // Contains a hyperlink to execute installation of the new system profiles. PresetUpdateAvailable, - // Not used. - LoadingFailed, +// LoadingFailed, // Not used - instead Slicing error is used for both slicing and validate errors. - ValidateError, +// ValidateError, // Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background // thread thowing a SlicingError exception. SlicingError, // Slicing warnings, issued by the slicing process. - // Slicing warnings are registered for a Print step or a PrintObject step, + // Slicing warnings are registered for a particular Print milestone or a PrintObject and its milestone. SlicingWarning, + // Object partially outside the print volume. Cannot slice. PlaterError, - PlaterWarning, - ApplyError - + // Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled. + PlaterWarning }; class NotificationManager @@ -121,6 +117,20 @@ private: const std::string text2 = std::string(); }; + // Cache of IDs to identify and reuse ImGUI windows. + class NotificationIDProvider + { + public: + int allocate_id(); + void release_id(int id); + + private: + // Next ID used for naming the ImGUI windows. + int m_next_id{ 1 }; + // IDs of ImGUI windows, which were released and they are ready for reuse. + std::vector m_released_ids; + }; + //Pop notification - shows only once to user. class PopNotification { @@ -133,8 +143,8 @@ private: Countdown, Hovered }; - PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler); - virtual ~PopNotification() = default; + PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); + virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width, bool move_from_slope, float slope_width); // close will dissapear notification on next render void close() { m_close_pending = true; } @@ -154,6 +164,7 @@ private: void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); void hide(bool h) { m_hidden = h; } + protected: // Call after every size change void init(); @@ -179,7 +190,8 @@ private: const NotificationData m_data; - int m_id; + NotificationIDProvider &m_id_provider; + int m_id { 0 }; bool m_initialized { false }; // Main text std::string m_text1; @@ -227,7 +239,7 @@ private: class SlicingCompleteLargeNotification : public PopNotification { public: - SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool largeds); + SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds); void set_large(bool l); bool get_large() { return m_is_large; } @@ -246,7 +258,7 @@ private: class SlicingWarningNotification : public PopNotification { public: - SlicingWarningNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler) : PopNotification(n, id, evt_handler) {} + SlicingWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} void set_object_id(size_t id) { object_id = id; } const size_t get_object_id() { return object_id; } void set_warning_step(int ws) { warning_step = ws; } @@ -265,25 +277,26 @@ private: void sort_notifications(); bool has_error_notification(); + // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. wxEvtHandler* m_evt_handler; + // Cache of IDs to identify and reuse ImGUI windows. + NotificationIDProvider m_id_provider; std::deque> m_pop_notifications; - int m_next_id { 1 }; long m_last_time { 0 }; bool m_hovered { false }; //timestamps used for slining finished - notification could be gone so it needs to be stored here std::unordered_set m_used_timestamps; bool m_in_preview { false }; bool m_move_from_overlay { false }; - bool m_move_from_slope{ false }; + bool m_move_from_slope { false }; //prepared (basic) notifications const std::vector basic_notifications = { - // Currently not used. - {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, +// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, - {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, - {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, +// {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, +// {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more.")}, {NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page.")}, //{NotificationType::NewAppAviable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },