From bcb2a4884b1e119907bc6898e01f4b8f094e9137 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Dec 2020 15:12:32 +0100 Subject: NotificationManager -> Separate notification update from its render to reduce the amount of scene refresh --- src/slic3r/GUI/GLCanvas3D.cpp | 24 +++- src/slic3r/GUI/NotificationManager.cpp | 242 ++++++++++++++++++++++++++++++++- src/slic3r/GUI/NotificationManager.hpp | 61 ++++++++- 3 files changed, 314 insertions(+), 13 deletions(-) (limited to 'src/slic3r') diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e2d3a9585..84b17efb7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1703,7 +1703,7 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); - wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); + wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); wxGetApp().imgui()->render(); @@ -2358,6 +2358,14 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + NotificationManager* notification_mgr = wxGetApp().plater()->get_notification_manager(); + if (notification_mgr->requires_update()) + notification_mgr->update_notifications(); + + m_dirty |= notification_mgr->requires_render(); +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); @@ -2365,12 +2373,24 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); m_dirty |= mouse3d_controller_applied; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + if (!m_dirty) { + if (notification_mgr->requires_update()) + evt.RequestMore(); + return; + } +#else if (!m_dirty) return; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT _refresh_if_shown_on_screen(); +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + if (m_extra_frame_requested || mouse3d_controller_applied || notification_mgr->requires_update()) { +#else if (m_extra_frame_requested || mouse3d_controller_applied) { +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT m_dirty = true; m_extra_frame_requested = false; evt.RequestMore(); @@ -2505,7 +2525,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case WXK_BACK: case WXK_DELETE: post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); break; - default: evt.Skip(); + default: evt.Skip(); } } else if ((evt.GetModifiers() & shiftMask) != 0) { diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 15c84af9e..bf4142b9c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -18,6 +18,9 @@ static constexpr float GAP_WIDTH = 10.0f; static constexpr float SPACE_RIGHT_PANEL = 10.0f; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +static constexpr float FADING_OUT_DURATION = 2.0f; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT namespace Slic3r { namespace GUI { @@ -134,6 +137,96 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, { //init(); } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) +{ + if (m_hidden) { + m_top_y = initial_y - GAP_WIDTH; + return; + } + + Size cnv_size = canvas.get_canvas_size(); + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImVec2 mouse_pos = ImGui::GetMousePos(); + float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); + + if (m_line_height != ImGui::CalcTextSize("A").y) + init(); + + set_next_window_size(imgui); + + // top y of window + m_top_y = initial_y + m_window_height; + + ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - right_gap, 1.0f * (float)cnv_size.get_height() - m_top_y); + imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); + imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); + + // find if hovered + m_hovered = false; + if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) { + ImGui::SetNextWindowFocus(); + m_hovered = true; + } + + // color change based on fading out + bool fading_pop = false; + if (m_fading_out) { + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity); + fading_pop = true; + } + + // background color + if (m_is_gray) { + ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + else if (m_data.level == NotificationLevel::ErrorNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + else if (m_data.level == NotificationLevel::WarningNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + backcolor.y += 0.15f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + } + + // name of window - probably indentifies window and is shown so last_end add whitespaces according to id + if (m_id == 0) + m_id = m_id_provider.allocate_id(); + std::string name = "!!Ntfctn" + std::to_string(m_id); + if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) { + ImVec2 win_size = ImGui::GetWindowSize(); + + //FIXME: dont forget to us this for texts + //GUI::format(_utf8(L())); + + /* + //countdown numbers + ImGui::SetCursorPosX(15); + ImGui::SetCursorPosY(15); + imgui.text(std::to_string(m_remaining_time).c_str()); + */ + + render_left_sign(imgui); + render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); + m_minimize_b_visible = false; + if (m_multiline && m_lines_count > 3) + render_minimize_button(imgui, win_pos.x, win_pos.y); + } + imgui.end(); + + if (m_is_gray || m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) + ImGui::PopStyleColor(); + + if (fading_pop) + ImGui::PopStyleColor(2); +} +#else NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width) { if (!m_initialized) { @@ -268,6 +361,7 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif ImGui::PopStyleColor(); return ret_val; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::count_spaces() { //determine line width @@ -528,6 +622,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImGui::PopStyleColor(); ImGui::PopStyleColor(); } +#if !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { /* @@ -575,6 +670,7 @@ void NotificationManager::PopNotification::render_countdown(ImGuiWrapper& imgui, m_countdown_frame++; */ } +#endif // !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui) { if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { @@ -643,6 +739,52 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::PopNotification::update_state() +{ + if (!m_initialized) + init(); + + if (m_hidden) { + m_state = EState::Static; + return; + } + + if (m_hovered) { + // reset fading + m_fading_out = false; + m_current_fade_opacity = 1.0f; + m_remaining_time = m_data.duration; + } + + if (m_counting_down) { + if (m_fading_out && m_current_fade_opacity <= 0.0f) + m_finished = true; + else if (!m_fading_out && m_remaining_time == 0) { + m_fading_out = true; + m_fading_start = wxGetLocalTimeMillis(); + } + } + + if (m_finished) { + m_state = EState::Finished; + return; + } + if (m_close_pending) { + m_finished = true; + m_state = EState::ClosePending; + return; + } + if (m_fading_out) { + if (!m_paused) { + wxMilliClock_t curr_time = wxGetLocalTimeMillis() - m_fading_start; + m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time.GetValue()) / FADING_OUT_DURATION, 0.0f, 1.0f); + } + m_state = EState::FadingOut; + } +} +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : NotificationManager::PopNotification(n, id_provider, evt_handler) { @@ -1031,6 +1173,10 @@ bool NotificationManager::push_notification_data(const NotificationData& notific } bool NotificationManager::push_notification_data(std::unique_ptr notification, int timestamp) { +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + m_requires_update = true; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + // if timestamped notif, push only new one if (timestamp != 0) { if (m_used_timestamps.find(timestamp) == m_used_timestamps.end()) { @@ -1052,6 +1198,21 @@ bool NotificationManager::push_notification_data(std::unique_ptrget_current_canvas3D(); + float last_y = 0.0f; + + for (const auto& notification : m_pop_notifications) { + notification->render(canvas, last_y, m_move_from_overlay && !m_in_preview, overlay_width); + if (notification->get_state() != PopNotification::EState::Finished) + last_y = notification->get_top() + GAP_WIDTH; + } +} +#else void NotificationManager::render_notifications(float overlay_width) { float last_x = 0.0f; @@ -1063,9 +1224,9 @@ void NotificationManager::render_notifications(float overlay_width) GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D(); - // iterate thru notifications and render them / erease them + // iterate thru notifications and render them / erase them for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { - if ((*it)->get_finished()) { + if ((*it)->is_finished()) { it = m_pop_notifications.erase(it); } else { (*it)->set_paused(m_hovered); @@ -1115,6 +1276,7 @@ void NotificationManager::render_notifications(float overlay_width) // If any of the notifications is fading out, 100% of the CPU/GPU is consumed. canvas.request_extra_frame(); } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT void NotificationManager::sort_notifications() { @@ -1122,7 +1284,7 @@ void NotificationManager::sort_notifications() std::stable_sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](const std::unique_ptr &n1, const std::unique_ptr &n2) { int n1l = (int)n1->get_data().level; int n2l = (int)n2->get_data().level; - if (n1l == n2l && n1->get_is_gray() && !n2->get_is_gray()) + if (n1l == n2l && n1->is_gray() && !n2->is_gray()) return true; return (n1l < n2l); }); @@ -1133,7 +1295,7 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi NotificationType new_type = notification->get_type(); const std::string &new_text = notification->get_data().text1; for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { - if ((*it)->get_type() == new_type && !(*it)->get_finished()) { + if ((*it)->get_type() == new_type && !(*it)->is_finished()) { if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) { if (!(*it)->compare_text(new_text)) continue; @@ -1166,6 +1328,78 @@ void NotificationManager::set_in_preview(bool preview) } } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT +void NotificationManager::update_notifications() +{ + static size_t last_size = 0; + + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + std::unique_ptr& notification = *it; + if (notification->get_state() == PopNotification::EState::Finished) + it = m_pop_notifications.erase(it); + else { + notification->set_paused(m_hovered); + notification->update_state(); + ++it; + } + } + + m_requires_update = false; + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->requires_update()) { + m_requires_update = true; + break; + } + } + + // update hovering state + m_hovered = false; + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->is_hovered()) { + m_hovered = true; + break; + } + } + + size_t curr_size = m_pop_notifications.size(); + m_requires_render = m_hovered || (last_size != curr_size); + last_size = curr_size; + + if (!m_requires_render) { + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->requires_render()) { + m_requires_render = true; + break; + } + } + } + + // actualizate timers + wxWindow* p = dynamic_cast(wxGetApp().plater()); + while (p->GetParent() != nullptr) + p = p->GetParent(); + wxTopLevelWindow* top_level_wnd = dynamic_cast(p); + if (!top_level_wnd->IsActive()) + return; + + { + // Control the fade-out. + // time in seconds + long now = wxGetLocalTime(); + // Pausing fade-out when the mouse is over some notification. + if (!m_hovered && m_last_time < now) { + if (now - m_last_time >= 1) { + for (auto& notification : m_pop_notifications) { + if (notification->get_state() != PopNotification::EState::Static) + notification->substract_remaining_time(); + } + } + m_last_time = now; + } + } +} +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool NotificationManager::has_slicing_error_notification() { return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index d8ca1e0bd..81c57eccb 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -140,7 +140,13 @@ public: void set_in_preview(bool preview); // Move to left to avoid colision with variable layer height gizmo. void set_move_from_overlay(bool move) { m_move_from_overlay = move; } - + +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void update_notifications(); + bool requires_update() const { return m_requires_update; } + bool requires_render() const { return m_requires_render; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + private: // duration 0 means not disapearing struct NotificationData { @@ -175,6 +181,17 @@ private: class PopNotification { public: +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + enum class EState + { + Unknown, + Static, + Countdown, + FadingOut, + ClosePending, + Finished + }; +#else enum class RenderResult { Finished, @@ -183,27 +200,41 @@ private: Countdown, Hovered }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); +#else RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width); +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // close will dissapear notification on next render void close() { m_close_pending = true; } // data from newer notification of same type void update(const NotificationData& n); - bool get_finished() const { return m_finished || m_close_pending; } + bool is_finished() const { return m_finished || m_close_pending; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool is_hovered() const { return m_hovered; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // returns top after movement float get_top() const { return m_top_y; } //returns top in actual frame float get_current_top() const { return m_top_y; } const NotificationType get_type() const { return m_data.type; } - const NotificationData get_data() const { return m_data; } - const bool get_is_gray() const { return m_is_gray; } + const NotificationData get_data() const { return m_data; } + const bool is_gray() const { return m_is_gray; } // Call equals one second down void substract_remaining_time() { m_remaining_time--; } void set_gray(bool g) { m_is_gray = g; } void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); void hide(bool h) { m_hidden = h; } +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + void update_state(); + bool requires_render() const { return m_fading_out || m_close_pending || m_finished; } + bool requires_update() const { return m_state != EState::Static; } + EState get_state() const { return m_state; } +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT protected: // Call after every size change @@ -218,9 +249,11 @@ private: virtual void render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); +#if !ENABLE_NEW_NOTIFICATIONS_FADE_OUT void render_countdown(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x , const float win_pos_y); +#endif // !ENABLE_NEW_NOTIFICATIONS_FADE_OUT virtual void render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, @@ -237,7 +270,10 @@ private: // For reusing ImGUI windows. NotificationIDProvider &m_id_provider; - int m_id { 0 }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + EState m_state { EState::Unknown }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + int m_id { 0 }; bool m_initialized { false }; // Main text std::string m_text1; @@ -252,15 +288,22 @@ private: bool m_paused { false }; int m_countdown_frame { 0 }; bool m_fading_out { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + wxMilliClock_t m_fading_start { 0LL }; +#else // total time left when fading beggins - float m_fading_time { 0.0f }; - float m_current_fade_opacity { 1.f }; + float m_fading_time{ 0.0f }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT + float m_current_fade_opacity { 1.0f }; // If hidden the notif is alive but not visible to user bool m_hidden { false }; // m_finished = true - does not render, marked to delete bool m_finished { false }; // Will go to m_finished next render bool m_close_pending { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool m_hovered { false }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT // variables to count positions correctly // all space without text float m_window_width_offset; @@ -390,6 +433,10 @@ private: bool m_in_preview { false }; // True if the layer editing is enabled in Plater, so that the notifications are shifted left of it. bool m_move_from_overlay { false }; +#if ENABLE_NEW_NOTIFICATIONS_FADE_OUT + bool m_requires_update{ false }; + bool m_requires_render{ false }; +#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT //prepared (basic) notifications const std::vector basic_notifications = { -- cgit v1.2.3