From 422ad1c5bf99032def5cd210c81fb0121417ab75 Mon Sep 17 00:00:00 2001
From: David Kocik <kocikdav@gmail.com>
Date: Tue, 10 Nov 2020 09:20:39 +0100
Subject: [PATCH] Callback stored at std::function for notification hyperlink.
 Notification progress bar draft.

---
 src/slic3r/GUI/NotificationManager.cpp | 81 ++++++++++++++++++--------
 src/slic3r/GUI/NotificationManager.hpp | 48 +++++++++++----
 src/slic3r/GUI/Plater.cpp              |  2 +-
 3 files changed, 96 insertions(+), 35 deletions(-)

diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index b3a1aa17b..e59d61d0a 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -587,8 +587,6 @@ void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
 }
 void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
 {
-	ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
-	orange_color.w = 0.8f;
 	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);
@@ -623,27 +621,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
 }
 bool NotificationManager::PopNotification::on_text_click()
 {
-	bool ret = true;
-	switch (m_data.type) {
-	case NotificationType::SlicingComplete :
-		//wxGetApp().plater()->export_gcode(false);
-		assert(m_evt_handler != nullptr);
-		if (m_evt_handler != nullptr)
-			wxPostEvent(m_evt_handler, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED));
-		break;
-	case NotificationType::PresetUpdateAvailable :
-		//wxGetApp().plater()->export_gcode(false);
-		assert(m_evt_handler != nullptr);
-		if (m_evt_handler != nullptr)
-			wxPostEvent(m_evt_handler, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
-		break;
-	case NotificationType::NewAppAvailable:
-		wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases");
-		break;
-	default:
-		break;
-	}
-	return ret;
+	if(m_data.callback != nullptr)
+		return m_data.callback(m_evt_handler);
+	return false;
 }
 void NotificationManager::PopNotification::update(const NotificationData& n)
 {
@@ -831,6 +811,39 @@ bool NotificationManager::ExportFinishedNotification::on_text_click()
 	Notifications_Internal::open_folder(m_export_dir_path);
 	return false;
 }
+//------ProgressBar----------------
+void NotificationManager::ProgressBarNotification::init()
+{
+	PopNotification::init();
+	m_lines_count++;
+	m_endlines.push_back(m_endlines.back());
+}
+void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
+{
+	PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+	render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+}
+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)
+{
+	float bar_y = win_size_y / 2 - win_size_y / 6 + m_line_height;
+	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);
+	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) :
 	m_evt_handler(evt_handler)
@@ -948,7 +961,8 @@ void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas,
 		hypertext = _u8L("Export G-Code.");
 		time = 0;
 	}
-	NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time,  _u8L("Slicing finished."), hypertext };
+	NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time,  _u8L("Slicing finished."), hypertext, [](wxEvtHandler* evnthndlr){
+		if (evnthndlr != nullptr) wxPostEvent(evnthndlr, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED)); return true; } };
 	push_notification_data(std::make_unique<NotificationManager::SlicingCompleteLargeNotification>(data, m_id_provider, m_evt_handler, large),
 		canvas, timestamp);
 }
@@ -994,6 +1008,25 @@ void NotificationManager::push_exporting_finished_notification(GLCanvas3D& canva
 	push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path),
 		canvas, 0);
 }
+void  NotificationManager::push_progress_bar_notification(const std::string& text, GLCanvas3D& canvas, float percentage)
+{
+	NotificationData data{ NotificationType::ProgressBar, NotificationLevel::ProgressBarNotification, 0, text };
+	push_notification_data(std::make_unique<NotificationManager::ProgressBarNotification>(data, m_id_provider, m_evt_handler, 0),canvas, 0);
+}
+void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage, GLCanvas3D& canvas)
+{
+	bool found = false;
+	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);
+			canvas.request_extra_frame();
+			found = true;
+		}
+	}
+	if (!found) {
+		push_progress_bar_notification(text, canvas, percentage);
+	}
+}
 bool NotificationManager::push_notification_data(const NotificationData &notification_data,  GLCanvas3D& canvas, int timestamp)
 {
 	return push_notification_data(std::make_unique<PopNotification>(notification_data, m_id_provider, m_evt_handler), canvas, timestamp);
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 0550dab9b..d483173c3 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -60,7 +60,9 @@ enum class NotificationType
 	// Object partially outside the print volume. Cannot slice.
 	PlaterError,
 	// Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled.
-	PlaterWarning
+	PlaterWarning,
+	// Progress bar instead of text.
+	ProgressBar
 };
 
 class NotificationManager
@@ -74,6 +76,8 @@ public:
 		RegularNotification = 1,
 		// Information notification without a fade-out or with a longer fade-out.
 		ImportantNotification,
+		// Important notification with progress bar, no fade-out, might appear again after closing.
+		ProgressBarNotification,
 		// Warning, no fade-out.
 		WarningNotification,
 		// Error, no fade-out.
@@ -121,7 +125,10 @@ public:
 	void set_slicing_complete_large(bool large);
 	// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
 	void push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable);
-    // Close old notification ExportFinished.
+	// notification with progress bar
+	void  push_progress_bar_notification(const std::string& text, GLCanvas3D& canvas, float percentage = 0);
+	void set_progress_bar_percentage(const std::string& text, float percentage, GLCanvas3D& canvas);
+	// Close old notification ExportFinished.
 	void new_export_began(bool on_removable);
 	// finds ExportFinished notification and closes it if it was to removable device
 	void device_ejected();
@@ -137,13 +144,15 @@ public:
 private:
 	// duration 0 means not disapearing
 	struct NotificationData {
-		NotificationType    type;
-		NotificationLevel   level;
+		NotificationType         type;
+		NotificationLevel        level;
 		// Fade out time
-		const int           duration;
-		const std::string   text1;
-		const std::string   hypertext;
-		const std::string   text2;
+		const int                duration;
+		const std::string        text1;
+		const std::string        hypertext;
+		// Callback for hypertext - returns if notif shall close.
+		std::function<bool(wxEvtHandler*)> callback { nullptr };
+		const std::string        text2;
 	};
 
 	// Cache of IDs to identify and reuse ImGUI windows.
@@ -301,6 +310,23 @@ private:
 		int    		warning_step;
 	};
 
+	class ProgressBarNotification : public PopNotification
+	{
+	public:
+		ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
+		void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; }
+	protected:
+		virtual void init();
+		virtual void render_text(ImGuiWrapper& imgui,
+			const float win_size_x, const float win_size_y,
+			const float win_pos_x, const float win_pos_y);
+		void         render_bar(ImGuiWrapper& imgui,
+			const float win_size_x, const float win_size_y,
+			const float win_pos_x, const float win_pos_y);
+		bool m_progress_complete{ false };
+		float m_percentage;
+	};
+
 	class ExportFinishedNotification : public PopNotification
 	{
 	public:
@@ -369,8 +395,10 @@ private:
 		{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::NewAppAvailable, NotificationLevel::ImportantNotification, 20,  _u8L("New version is available."),  _u8L("See Releases page.")},
+		{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20,  _u8L("Configuration update is available."),  _u8L("See more."), [](wxEvtHandler* evnthndlr){
+			if (evnthndlr != nullptr) wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); return true; }},
+		{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20,  _u8L("New version is available."),  _u8L("See Releases page."), [](wxEvtHandler* evnthndlr){ 
+				wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
 		//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20,  _u8L("New vesion of PrusaSlicer is available.",  _u8L("Download page.") },
 		//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20,  _u8L("Loading of model has Failed") },
 		//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10,  _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 6b7b2214c..43c968d83 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3456,6 +3456,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
 
         this->statusbar()->set_progress(evt.status.percent);
         this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8("…"));
+        //notification_manager->set_progress_bar_percentage("Slicing progress", (float)evt.status.percent / 100.0f, *q->get_current_canvas3D());
     }
     if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE | PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
         switch (this->printer_technology) {
@@ -3507,7 +3508,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
 void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
 {
 	notification_manager->push_slicing_complete_notification(*q->get_current_canvas3D(), evt.GetInt(), is_sidebar_collapsed());
-
     switch (this->printer_technology) {
     case ptFFF:
         this->update_fff_scene();