From c41df487bb5821a9271e547ab97d424e1e9b6afa Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Mon, 8 Feb 2021 16:14:35 +0100
Subject: [PATCH] Notifications management and rendering  refactoring.

With warning notification Model out of bed reworked to not show after dismiss.
---
 src/slic3r/GUI/3DScene.cpp             |  51 ++++
 src/slic3r/GUI/3DScene.hpp             |   1 +
 src/slic3r/GUI/GLCanvas3D.cpp          |  65 +++--
 src/slic3r/GUI/GLCanvas3D.hpp          |   3 +-
 src/slic3r/GUI/NotificationManager.cpp | 385 ++++++++++---------------
 src/slic3r/GUI/NotificationManager.hpp | 112 ++++---
 6 files changed, 293 insertions(+), 324 deletions(-)

diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 4cab8f3db..6c226cd74 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -845,6 +845,57 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
     return contained_min_one;
 }
 
+bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut)
+{
+    if (config == nullptr)
+        return false;
+
+    const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
+    if (opt == nullptr)
+        return false;
+
+    BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
+    BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
+    // Allow the objects to protrude below the print bed
+    print_volume.min(2) = -1e10;
+    print_volume.min(0) -= BedEpsilon;
+    print_volume.min(1) -= BedEpsilon;
+    print_volume.max(0) += BedEpsilon;
+    print_volume.max(1) += BedEpsilon;
+
+    bool contained_min_one = false;
+
+    partlyOut = false;
+    fullyOut = false;
+    for (GLVolume* volume : this->volumes)
+    {
+        if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
+            continue;
+
+        const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
+        bool contained = print_volume.contains(bb);
+
+        volume->is_outside = !contained;
+        if (!volume->printable)
+            continue;
+
+        if (contained)
+            contained_min_one = true;
+
+        if (volume->is_outside) {
+            if (print_volume.intersects(bb))
+                partlyOut = true;
+            else 
+                fullyOut = true;
+        }
+    }
+    /*
+    if (out_state != nullptr)
+        *out_state = state;
+    */
+    return contained_min_one;
+}
+
 void GLVolumeCollection::reset_outside_state()
 {
     for (GLVolume* volume : this->volumes)
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index e4d9c6067..2ae2a36b2 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -569,6 +569,7 @@ public:
     // returns true if all the volumes are completely contained in the print volume
     // returns the containment state in the given out_state, if non-null
     bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state);
+    bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut);
     void reset_outside_state();
 
     void update_colors_by_extruder(const DynamicPrintConfig* config);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index a45f61cab..0a2b5cd65 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -641,6 +641,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
         error = true;
         break;
     }
+    BOOST_LOG_TRIVIAL(error) << state << " : " << text ;
     auto &notification_manager = *wxGetApp().plater()->get_notification_manager();
     if (state) {
         if(error)
@@ -1620,9 +1621,6 @@ void GLCanvas3D::render()
         wxGetApp().plater()->init_environment_texture();
 #endif // ENABLE_ENVIRONMENT_MAP
 
-    m_render_timer.Stop();
-    m_extra_frame_requested_delayed = std::numeric_limits<int>::max();
-
     const Size& cnv_size = get_canvas_size();
     // Probably due to different order of events on Linux/GTK2, when one switched from 3D scene
     // to preview, this was called before canvas had its final size. It reported zero width
@@ -1754,7 +1752,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(*this, get_overlay_window_width());
 
     wxGetApp().imgui()->render();
 
@@ -2238,24 +2236,24 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
 
     // checks for geometry outside the print volume to render it accordingly
     if (!m_volumes.empty()) {
-        ModelInstanceEPrintVolumeState state;
-
-        const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
+        bool partlyOut = false;
+        bool fullyOut = false;
+        const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut);
 
 #if ENABLE_WARNING_TEXTURE_REMOVAL
-        _set_warning_notification(EWarning::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
-        _set_warning_notification(EWarning::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
-        if (printer_technology != ptSLA || state == ModelInstancePVS_Inside)
+        _set_warning_notification(EWarning::ObjectClashed, partlyOut);
+        _set_warning_notification(EWarning::ObjectOutside, fullyOut);
+        if (printer_technology != ptSLA || !contained_min_one)
             _set_warning_notification(EWarning::SlaSupportsOutside, false);
 #else
-        _set_warning_texture(WarningTexture::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
-        _set_warning_texture(WarningTexture::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
-        if(printer_technology != ptSLA || state == ModelInstancePVS_Inside)
+        _set_warning_texture(WarningTexture::ObjectClashed, partlyOut);
+        _set_warning_texture(WarningTexture::ObjectOutside, fullyOut);
+        if(printer_technology != ptSLA || !contained_min_one)
             _set_warning_texture(WarningTexture::SlaSupportsOutside, false);
 #endif // ENABLE_WARNING_TEXTURE_REMOVAL
 
         post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, 
-                               contained_min_one && !m_model->objects.empty() && state != ModelInstancePVS_Partly_Outside));
+                               contained_min_one && !m_model->objects.empty() && !partlyOut));
     }
     else {
 #if ENABLE_WARNING_TEXTURE_REMOVAL
@@ -2442,13 +2440,13 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
     if (!m_initialized)
         return;
 
-    // FIXME
     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();
     m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
     bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
     m_dirty |= mouse3d_controller_applied;
+    m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
 
     if (!m_dirty)
         return;
@@ -2982,30 +2980,39 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
 
 void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
 {
-    // If slicer is not top window -> restart timer with one second to try again
-    wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
-    while (p->GetParent() != nullptr)
-        p = p->GetParent();
-    wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
-    if (!top_level_wnd->IsActive()) {
-        request_extra_frame_delayed(1000);
-        return;
-    }
-    //render();
-    m_dirty = true;
-    wxWakeUpIdle();
+    // no need to do anything here
+    // right after this event is recieved, idle event is fired
+
+    //m_dirty = true;
+    //wxWakeUpIdle();  
 }
 
-void GLCanvas3D::request_extra_frame_delayed(int miliseconds)
+
+void GLCanvas3D::schedule_extra_frame(int miliseconds)
 {
+    // Schedule idle event right now
+    if (miliseconds == 0)
+    {
+        // We want to wakeup idle evnt but most likely this is call inside render cycle so we need to wait
+        if (m_in_render)
+            miliseconds = 33;
+        else {
+            m_dirty = true;
+            wxWakeUpIdle();
+            return;
+        }
+    } 
+    // Start timer
     int64_t now = timestamp_now();
+    // Timer is not running
     if (! m_render_timer.IsRunning()) {
         m_extra_frame_requested_delayed = miliseconds;
         m_render_timer.StartOnce(miliseconds);
         m_render_timer_start = now;
+    // Timer is running - restart only if new period is shorter than remaning period
     } else {
         const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now;
-        if (miliseconds < remaining_time) {
+        if (miliseconds + 20 < remaining_time) {
             m_render_timer.Stop(); 
             m_extra_frame_requested_delayed = miliseconds;
             m_render_timer.StartOnce(miliseconds);
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 10294931f..f4d862b66 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -743,7 +743,8 @@ public:
     void msw_rescale();
 
     void request_extra_frame() { m_extra_frame_requested = true; }
-    void request_extra_frame_delayed(int miliseconds);
+    
+    void schedule_extra_frame(int miliseconds);
 
     int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
     void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index a6fd9cfd3..41c8e8f6f 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -22,9 +22,6 @@ static constexpr float SPACE_RIGHT_PANEL = 10.0f;
 static constexpr float FADING_OUT_DURATION = 2.0f;
 // Time in Miliseconds after next render when fading out is requested
 static constexpr int   FADING_OUT_TIMEOUT = 100;
-// If timeout is changed to higher than 1 second, substract_time call should be revorked
-//static constexpr int   MAX_TIMEOUT_MILISECONDS = 1000; 
-//static constexpr int   MAX_TIMEOUT_SECONDS = 1;
 
 namespace Slic3r {
 namespace GUI {
@@ -131,35 +128,29 @@ void NotificationManager::NotificationIDProvider::release_id(int) {}
 NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) :
 	  m_data                (n)
 	, m_id_provider   		(id_provider)
-	, m_remaining_time      (n.duration)
-	, m_last_remaining_time (n.duration)
-	, m_counting_down       (n.duration != 0)
 	, m_text1               (n.text1)
     , m_hypertext           (n.hypertext)
     , m_text2               (n.text2)
 	, m_evt_handler         (evt_handler)
 	, m_notification_start  (GLCanvas3D::timestamp_now())
-{
-	//init();
-}
+{}
 
 void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width)
 {
-	if (!m_initialized)
+
+	if (m_state == EState::Unknown)
 		init();
 
-	if (m_hidden) {
+	if (m_state == EState::Hidden) {
 		m_top_y = initial_y - GAP_WIDTH;
 		return;
 	}
 
-	if (m_fading_out) 
-		m_last_render_fading = GLCanvas3D::timestamp_now();
-
-	Size cnv_size = canvas.get_canvas_size();
+	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);
+	ImVec2        mouse_pos = ImGui::GetMousePos();
+	float         right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
+	bool          fading_pop = false;
 
 	if (m_line_height != ImGui::CalcTextSize("A").y)
 		init();
@@ -174,54 +165,46 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
 	imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
 
 	// find if hovered
-	m_hovered = false;
+	if (m_state == EState::Hovered) 
+		m_state = EState::Shown;
+
 	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;
+		m_state = EState::Hovered;
 	}
-
+	
 	// 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);
+	if (m_state == EState::FadingOut) {
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
+		Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, 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);
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, 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);
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, 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);
+		Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
 	}
-
-	// name of window - probably indentifies window and is shown so last_end add whitespaces according to id
+	
+	// name of window indentifies window - has to be unique string
 	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);
@@ -253,6 +236,7 @@ void NotificationManager::PopNotification::count_spaces()
 	m_window_width_offset = m_left_indentation + m_line_height * 3.f;
 	m_window_width = m_line_height * 25;
 }
+ 
 void NotificationManager::PopNotification::init()
 {
     std::string text          = m_text1 + " " + m_hypertext;
@@ -306,7 +290,7 @@ void NotificationManager::PopNotification::init()
 	}
 	if (m_lines_count == 3)
 		m_multiline = true;
-	m_initialized = true;
+	m_state = EState::Shown;
 }
 void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
 { 
@@ -423,8 +407,8 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
 			m_multiline = true;
 			set_next_window_size(imgui);
 		}
-		else {
-			m_close_pending = on_text_click();
+		else if (on_text_click()) {
+			close();
 		}
 	}
 	ImGui::PopStyleColor();
@@ -432,12 +416,12 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
 	ImGui::PopStyleColor();
 
 	//hover color
-	ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button);
+	ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
 	if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
 		orange_color.y += 0.2f;
 
 	//text
-	Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
 	ImGui::SetCursorPosX(text_x);
 	ImGui::SetCursorPosY(text_y);
 	imgui.text(text.c_str());
@@ -448,7 +432,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
 	lineEnd.y -= 2;
 	ImVec2 lineStart = lineEnd;
 	lineStart.x = ImGui::GetItemRectMin().x;
-	ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))));
+	ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
 
 }
 
@@ -458,12 +442,11 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
 	ImVec2 win_pos(win_pos_x, win_pos_y); 
 	ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
 	ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
-	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
-	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
 	ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
 
 
-	//button - if part if treggered
 	std::string button_text;
 	button_text = ImGui::CloseNotifButton;
 	
@@ -479,7 +462,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
 	ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
 	if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
 	{
-		m_close_pending = true;
+		close();
 	}
 
 	//invisible large button
@@ -487,7 +470,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
 	ImGui::SetCursorPosY(0);
 	if (imgui.button(" ", m_line_height * 2.125, win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0)))
 	{
-		m_close_pending = true;
+		close();
 	}
 	ImGui::PopStyleColor();
 	ImGui::PopStyleColor();
@@ -510,9 +493,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
 {
 	ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
 	ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
-	Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
-	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
-	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
 
 	
 	//button - if part if treggered
@@ -564,71 +547,56 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text)
 	return false;
 }
 
-void NotificationManager::PopNotification::update_state()
+void NotificationManager::PopNotification::update_state(bool paused, const int64_t delta)
 {
-	if (!m_initialized)
+	if (m_state == EState::Unknown)
 		init();
 
 	m_next_render = std::numeric_limits<int64_t>::max();
 
-	if (m_hidden) {
-		m_state = EState::Hidden;
+	if (m_state == EState::Hidden) {
 		return;
 	}
 
 	int64_t now = GLCanvas3D::timestamp_now();
 
-	if (m_hovered) {
-		// reset fading
-		m_fading_out = false;
+	// reset timers - hovered state is set in render 
+	if (m_state == EState::Hovered) { 
 		m_current_fade_opacity = 1.0f;
-		m_remaining_time = m_data.duration;
 		m_notification_start = now;
-	}
-
-	
-
-	if (m_counting_down) {
+	// Timers when not fading
+	} else if (m_data.duration != 0 && !paused) {
 		int64_t up_time = now - m_notification_start;
-
-		if (m_fading_out && m_current_fade_opacity <= 0.0f)
-			m_finished = true;
-		else if (!m_fading_out && /*m_remaining_time <=0*/up_time >= m_data.duration * 1000) {
-			m_fading_out = true;
-			m_fading_start = now;
-			m_last_render_fading = now;
-		} else if (!m_fading_out) {
-			m_next_render = m_data.duration * 1000 - up_time;//std::min<int64_t>(/*m_data.duration * 1000 - up_time*/m_remaining_time * 1000, MAX_TIMEOUT_MILISECONDS);
-		}
-		
+		if (m_state != EState::FadingOut && up_time >= m_data.duration * 1000) {
+			m_state					= EState::FadingOut;
+			m_fading_start			= now;
+		} else if (m_state != EState::FadingOut) {
+			m_next_render = m_data.duration * 1000 - up_time;
+		}	
 	}
-	
-	if (m_finished) {
+	// Timers when fading
+	if (m_state == EState::FadingOut && !paused) {
+		int64_t curr_time		= now - m_fading_start;
+		int64_t next_render		= FADING_OUT_TIMEOUT - delta;
+		m_current_fade_opacity	= std::clamp(1.0f - 0.001f * static_cast<float>(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
+		if (m_current_fade_opacity <= 0.0f)
+			m_state = EState::Finished;
+		else if (next_render < 0) 
+			m_next_render = 0;
+		else
+			m_next_render = next_render;
+	}
+
+	if (m_state == EState::Finished) {
+		m_next_render = 0;
+		return;
+	}
+
+	if (m_state == EState::ClosePending) {
 		m_state = EState::Finished;
 		m_next_render = 0;
 		return;
 	}
-	if (m_close_pending) {
-		m_finished = true;
-		m_state = EState::ClosePending;
-		m_next_render = 0;
-		return;
-	}
-	if (m_fading_out) {
-		if (!m_paused) {
-			m_state = EState::FadingOutStatic;
-			int64_t curr_time      = now - m_fading_start;
-			int64_t no_render_time = now - m_last_render_fading;
-			m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast<float>(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
-			auto next_render = FADING_OUT_TIMEOUT - no_render_time;
-			if (next_render <= 0) {
-				//m_last_render_fading = GLCanvas3D::timestamp_now();
-				m_state = EState::FadingOutRender;
-				m_next_render = 0;
-			} else 
-				m_next_render = next_render;
-		}
-	}
 }
 
 NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) :
@@ -672,9 +640,10 @@ void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const
 void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
 {
 	m_is_large = l;
-	m_counting_down = !l;
+	//FIXME this information should not be lost (change m_data?)
+//	m_counting_down = !l;
 	m_hypertext = l ? _u8L("Export G-Code.") : std::string();
-	m_hidden = !l;
+	m_state = l ? EState::Shown : EState::Hidden;
 }
 //---------------ExportFinishedNotification-----------
 void NotificationManager::ExportFinishedNotification::count_spaces()
@@ -733,8 +702,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
 	ImVec2 win_pos(win_pos_x, win_pos_y);
 	ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
 	ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
-	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
-	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
+	Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
 	ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
 
 	std::string button_text;
@@ -768,7 +737,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
 		assert(m_evt_handler != nullptr);
 		if (m_evt_handler != nullptr)
 			wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
-		m_close_pending = true;
+		close();
 	}
 
 	//invisible large button
@@ -779,7 +748,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
 		assert(m_evt_handler != nullptr);
 		if (m_evt_handler != nullptr)
 			wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
-		m_close_pending = true;
+		close();
 	}
 	ImGui::PopStyleColor();
 	ImGui::PopStyleColor();
@@ -807,22 +776,12 @@ void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& img
 void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
 {
 	ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
-	float  invisible_length = 0;//((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
-	//invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
+	float  invisible_length = 0;
+
 	ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y/2 + m_line_height / 2);
 	ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y/2 + m_line_height / 2);
 	ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f);
-	/*
-	//countdown line
-	ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
-	float  invisible_length = ((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
-	invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
-	ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length, win_pos_y + win_size_y - 5);
-	ImVec2 lineStart = ImVec2(win_pos_x - win_size_x, win_pos_y + win_size_y - 5);
-	ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.picture_width * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))), 2.f);
-	if (!m_paused)
-		m_countdown_frame++;
-		*/
+
 }
 //------NotificationManager--------
 NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
@@ -881,12 +840,7 @@ void NotificationManager::push_plater_error_notification(const std::string& text
 {
 	push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0,  _u8L("ERROR:") + "\n" + text }, 0);
 }
-void NotificationManager::push_plater_warning_notification(const std::string& text)
-{
-	push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0,  _u8L("WARNING:") + "\n" + text }, 0);
-	// dissaper if in preview
-	set_in_preview(m_in_preview);
-}
+
 void NotificationManager::close_plater_error_notification(const std::string& text)
 {
 	for (std::unique_ptr<PopNotification> &notification : m_pop_notifications) {
@@ -895,11 +849,32 @@ void NotificationManager::close_plater_error_notification(const std::string& tex
 		}
 	}
 }
+
+void NotificationManager::push_plater_warning_notification(const std::string& text)
+{
+	// Find if was not hidden 
+	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
+		if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
+			if (notification->get_state() == PopNotification::EState::Hidden) {
+				//dynamic_cast<PlaterWarningNotification*>(notification.get())->show();
+				return;
+			}
+		}
+	}
+
+	NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0,  _u8L("WARNING:") + "\n" + text };
+
+	auto notification = std::make_unique<NotificationManager::PlaterWarningNotification>(data, m_id_provider, m_evt_handler);
+	push_notification_data(std::move(notification), 0);
+	// dissaper if in preview
+	set_in_preview(m_in_preview);
+}
+
 void NotificationManager::close_plater_warning_notification(const std::string& text)
 {
 	for (std::unique_ptr<PopNotification> &notification : m_pop_notifications) {
 		if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
-			notification->close();
+			dynamic_cast<PlaterWarningNotification*>(notification.get())->real_close();
 		}
 	}
 }
@@ -1008,7 +983,8 @@ void NotificationManager::set_progress_bar_percentage(const std::string& text, f
 	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
 		if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) {
 			dynamic_cast<ProgressBarNotification*>(notification.get())->set_percentage(percentage);
-			wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
+			// FIX ME: this is massive gpu eater (render every frame)
+			wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
 			found = true;
 		}
 	}
@@ -1035,20 +1011,19 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
 
 	if (this->activate_existing(notification.get())) {
 		m_pop_notifications.back()->update(notification->get_data());
-		canvas.request_extra_frame_delayed(33);
+		canvas.schedule_extra_frame(0);
 		return false;
 	} else {
 		m_pop_notifications.emplace_back(std::move(notification));
-		canvas.request_extra_frame_delayed(33);
+		canvas.schedule_extra_frame(0);
 		return true;
 	}
 }
 
-void NotificationManager::render_notifications(float overlay_width)
+void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width)
 {
 	sort_notifications();
-
-	GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D();
+	
 	float last_y = 0.0f;
 
 	for (const auto& notification : m_pop_notifications) {
@@ -1059,9 +1034,54 @@ void NotificationManager::render_notifications(float overlay_width)
 		}
 		
 	}
-	update_notifications();
+	m_last_render = GLCanvas3D::timestamp_now();
 }
 
+bool NotificationManager::update_notifications(GLCanvas3D& canvas)
+{
+	// no update if not top window
+	wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
+	while (p->GetParent() != nullptr)
+		p = p->GetParent();
+	wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
+	if (!top_level_wnd->IsActive())
+		return false;
+
+	// next_render() returns numeric_limits::max if no need for frame
+	const int64_t max = std::numeric_limits<int64_t>::max();
+	int64_t       next_render = max;
+	const int64_t time_since_render = GLCanvas3D::timestamp_now() - m_last_render;
+	// During render, each notification detects if its currently hovered and changes its state to EState::Hovered
+	// If any notification is hovered, all restarts its countdown 
+	bool          hover = false;
+	for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
+		if (notification->is_hovered()) {
+			hover = true;
+			break;
+		}
+	}
+	// update state of all notif and erase finished
+	for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
+		std::unique_ptr<PopNotification>& notification = *it;
+		notification->update_state(hover, time_since_render);
+		next_render = std::min<int64_t>(next_render, notification->next_render());
+		if (notification->get_state() == PopNotification::EState::Finished)
+			it = m_pop_notifications.erase(it);
+		else 
+			++it;
+	}
+
+	// render needed right now
+	if (next_render < 20)
+		return true;
+	// request next frame 
+	if (next_render < max)
+		canvas.schedule_extra_frame(int(next_render));
+
+	return false;
+}
+>>>>>>> 6df0d8ff81... Notifications management and rendering  refactoring.
+
 void NotificationManager::sort_notifications()
 {
 	// Stable sorting, so that the order of equal ranges is stable.
@@ -1112,103 +1132,6 @@ void NotificationManager::set_in_preview(bool preview)
     }
 }
 
-void NotificationManager::update_notifications()
-{
-	// no update if not top window
-	wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
-	while (p->GetParent() != nullptr)
-		p = p->GetParent();
-	wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
-	if (!top_level_wnd->IsActive())
-		return;
-
-	//static size_t last_size = m_pop_notifications.size();
-
-	//request frames
-	int64_t next_render = std::numeric_limits<int64_t>::max();
-	for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
-		std::unique_ptr<PopNotification>& notification = *it;
-		notification->set_paused(m_hovered);
-		notification->update_state();
-		next_render = std::min<int64_t>(next_render, notification->next_render());
-		if (notification->get_state() == PopNotification::EState::Finished)
-			it = m_pop_notifications.erase(it);
-		else {
-			
-			++it;
-		}
-	}
-	/*
-	m_requires_update = false;
-	for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-		if (notification->requires_update()) {
-			m_requires_update = true;
-			break;
-		}
-	}
-	*/
-	// update hovering state
-	m_hovered = false;
-	for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-		if (notification->is_hovered()) {
-			m_hovered = true;
-			break;
-		}
-	}
-
-	/*
-	// Reuire render if some notification was just deleted.
-	size_t curr_size = m_pop_notifications.size();
-	m_requires_render = m_hovered || (last_size != curr_size);
-	last_size = curr_size;
-
-	// Ask notification if it needs render
-	if (!m_requires_render) {
-		for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
-			if (notification->requires_render()) {
-				m_requires_render = true;
-				break;
-			}
-		}
-	}
-	// Make sure there will be update after last notification erased
-	if (m_requires_render)
-		m_requires_update = true;
-	*/
-	
-
-	if (next_render == 0)
-		wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render
-	else if (next_render < std::numeric_limits<int64_t>::max())
-		wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
-
-	/*
-	// actualizate timers
-	wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
-	while (p->GetParent() != nullptr)
-		p = p->GetParent();
-	wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(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 >= MAX_TIMEOUT_SECONDS) {
-				for (auto& notification : m_pop_notifications) {
-					//if (notification->get_state() != PopNotification::EState::Static)
-						notification->substract_remaining_time(MAX_TIMEOUT_SECONDS);
-				}
-				m_last_time = now;
-			}
-		}
-	}
-	*/
-}
-
 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 62c4ea845..4c584d366 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -147,14 +147,15 @@ public:
 	// finds ExportFinished notification and closes it if it was to removable device
 	void device_ejected();
 	// renders notifications in queue and deletes expired ones
-	void render_notifications(float overlay_width);
+	void render_notifications(GLCanvas3D& canvas, float overlay_width);
 	// finds and closes all notifications of given type
 	void close_notification_of_type(const NotificationType type);
 	// Which view is active? Plater or G-code preview? Hide warnings in G-code preview.
     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; }
-
+	// perform update_state on each notification and ask for more frames if needed, return true for render needed
+	bool update_notifications(GLCanvas3D& canvas);
 private:
 	// duration 0 means not disapearing
 	struct NotificationData {
@@ -192,23 +193,24 @@ private:
 
 		enum class EState
 		{
-			Unknown,
+			Unknown,		  // NOT initialized
 			Hidden,
-			FadingOutRender,  // Requesting Render
-			FadingOutStatic,
+			Shown,			  // Requesting Render at some time if duration != 0
+			FadingOut,        // Requesting Render at some time
 			ClosePending,     // Requesting Render
 			Finished,         // Requesting Render
+			Hovered,		  // Followed by Shown 
+			Paused
 		};
 
 		PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler);
 		virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); }
 		void                   render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width);
 		// close will dissapear notification on next render
-		void                   close() { m_close_pending = true; }
+		virtual void           close() { m_state = EState::ClosePending; }
 		// data from newer notification of same type
 		void                   update(const NotificationData& n);
-		bool                   is_finished() const { return m_finished || m_close_pending; }
-		bool                   is_hovered() const { return m_hovered; }
+		bool                   is_finished() const { return m_state == EState::ClosePending || m_state == EState::Finished; }
 		// returns top after movement
 		float                  get_top() const { return m_top_y; }
 		//returns top in actual frame
@@ -216,21 +218,15 @@ private:
 		const NotificationType get_type() const { return m_data.type; }
 		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(int seconds) { m_remaining_time -= seconds; }
 		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; }
-		// sets m_next_render with time of next mandatory rendering
-		void                   update_state();
-		int64_t 		       next_render() const { return m_next_render; }
-		/*
-		bool				   requires_render() const { return m_state == EState::FadingOutRender || m_state == EState::ClosePending || m_state == EState::Finished; }
-		bool				   requires_update() const { return m_state != EState::Hidden; }
-		*/
-		EState                 get_state() const { return m_state; }
-	protected:
+        void                   hide(bool h) {  m_state = h ? EState::Hidden : EState::Unknown; }
+		// sets m_next_render with time of next mandatory rendering. Delta is time since last render.
+		void                   update_state(bool paused, const int64_t delta);
+		int64_t 		       next_render() const { return is_finished() ? 0 : m_next_render; }
+		EState                 get_state()  const { return m_state; }
+		bool				   is_hovered() const { return m_state == EState::Hovered; } 
+	
 		// Call after every size change
 		void         init();
 		// Part of init() 
@@ -254,45 +250,36 @@ private:
 		// Hypertext action, returns true if notification should close.
 		// Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)>
 		virtual bool on_text_click();
-
+	protected:
 		const NotificationData m_data;
-
 		// For reusing ImGUI windows.
 		NotificationIDProvider &m_id_provider;
+		int              m_id{ 0 };
 
+		// State for rendering
 		EState           m_state                { EState::Unknown };
 
-		int              m_id                   { 0 };
-		bool			 m_initialized          { false };
+		// Time values for rendering fade-out
+
+		int64_t		 	 m_fading_start{ 0LL };
+
+		// first appereance of notification or last hover;
+		int64_t		 	 m_notification_start;
+		// time to next must-do render
+		int64_t          m_next_render{ std::numeric_limits<int64_t>::max() };
+		float            m_current_fade_opacity{ 1.0f };
+
+		// Notification data
+
 		// Main text
 		std::string      m_text1;
 		// Clickable text
 		std::string      m_hypertext;
 		// Aditional text after hypertext - currently not used
 		std::string      m_text2;
-		// Countdown variables
-		long             m_remaining_time;
-		bool             m_counting_down;
-		long             m_last_remaining_time;
-		bool             m_paused               { false };
-		int              m_countdown_frame      { 0 };
-		bool             m_fading_out           { false };
-		int64_t		 	 m_fading_start         { 0LL };
-		// time of last done render when fading
-		int64_t		 	 m_last_render_fading   { 0LL };
-		// first appereance of notification or last hover;
-		int64_t		 	 m_notification_start;
-		// time to next must-do render
-		int64_t          m_next_render          { std::numeric_limits<int64_t>::max() };
-		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 };
-		bool             m_hovered              { false };
-		// variables to count positions correctly
+
+		// inner variables to position notification window, texts and buttons correctly
+
 		// all space without text
 		float            m_window_width_offset;
 		// Space on left side without text
@@ -302,9 +289,7 @@ private:
 		float            m_window_width         { 450.0f };
 		//Distance from bottom of notifications to top of this notification
 		float            m_top_y                { 0.0f };  
-		
-		// Height of text
-		// Used as basic scaling unit!
+		// Height of text - Used as basic scaling unit!
 		float            m_line_height;
         std::vector<size_t> m_endlines;
 		// Gray are f.e. eorrors when its uknown if they are still valid
@@ -344,6 +329,15 @@ private:
 		int    		warning_step;
 	};
 
+	class PlaterWarningNotification : public PopNotification
+	{
+	public:
+		PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
+		virtual void close()      { m_state = EState::Hidden; }
+		void		 real_close() { m_state = EState::ClosePending; }
+		void         show()       { m_state = EState::Unknown; }
+	};
+
 	class ProgressBarNotification : public PopNotification
 	{
 	public:
@@ -405,33 +399,25 @@ private:
 	void sort_notifications();
 	// If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed.
     bool has_slicing_error_notification();
-	// perform update_state on each notification and ask for more frames if needed
-	void update_notifications();
-
+    
 	// 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<std::unique_ptr<PopNotification>> m_pop_notifications;
-	// Last render time in seconds for fade out control.
-	long                         m_last_time { 0 };
-	// When mouse hovers over some notification, the fade-out of all notifications is suppressed.
-	bool                         m_hovered { false };
 	//timestamps used for slicing finished - notification could be gone so it needs to be stored here
 	std::unordered_set<int>      m_used_timestamps;
 	// True if G-code preview is active. False if the Plater is active.
 	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 };
+	// Timestamp of last rendering
+	int64_t						 m_last_render { 0LL };
 
 	//prepared (basic) notifications
 	const std::vector<NotificationData> basic_notifications = {
-//		{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::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20,  _u8L("Configuration update is available."),  _u8L("See more."),
+        {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 10,  _u8L("Configuration update is available."),  _u8L("See more."),
              [](wxEvtHandler* evnthndlr) {
                  if (evnthndlr != nullptr)
                      wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));