diff --git a/resources/icons/cancel.svg b/resources/icons/cancel.svg
new file mode 100644
index 000000000..da44606a0
--- /dev/null
+++ b/resources/icons/cancel.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/resources/icons/cross_focus_large.svg b/resources/icons/cross_focus_large.svg
new file mode 100644
index 000000000..c246f2bd9
--- /dev/null
+++ b/resources/icons/cross_focus_large.svg
@@ -0,0 +1,81 @@
+
+
diff --git a/resources/icons/timer_dot.svg b/resources/icons/timer_dot.svg
new file mode 100644
index 000000000..3a77962b6
--- /dev/null
+++ b/resources/icons/timer_dot.svg
@@ -0,0 +1,72 @@
+
+
diff --git a/resources/icons/timer_dot_empty.svg b/resources/icons/timer_dot_empty.svg
new file mode 100644
index 000000000..a8e776b49
--- /dev/null
+++ b/resources/icons/timer_dot_empty.svg
@@ -0,0 +1,73 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index d32f64aa4..feda857ae 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -113,7 +113,12 @@ namespace ImGui
const char PrinterSlaIconMarker = 0x6;
const char FilamentIconMarker = 0x7;
const char MaterialIconMarker = 0x8;
-
+ const char CloseIconMarker = 0xB;
+ const char CloseIconHoverMarker = 0xC;
+ const char TimerDotMarker = 0xE;
+ const char TimerDotEmptyMarker = 0xF;
+ const char WarningMarker = 0x10;
+ const char ErrorMarker = 0x11;
// void MyFunction(const char* name, const MyMatrix44& v);
}
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 35dc5a53b..7d8067718 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -686,6 +686,7 @@ std::vector>> GCode::collec
std::sort(ordering.begin(), ordering.end(), [](const OrderingItem &oi1, const OrderingItem &oi2) { return oi1.print_z < oi2.print_z; });
std::vector>> layers_to_print;
+
// Merge numerically very close Z values.
for (size_t i = 0; i < ordering.size();) {
// Find the last layer with roughly the same print_z.
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 7e02c0fdd..57e84f71e 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -163,6 +163,8 @@ set(SLIC3R_GUI_SOURCES
GUI/InstanceCheck.hpp
GUI/Search.cpp
GUI/Search.hpp
+ GUI/NotificationManager.cpp
+ GUI/NotificationManager.hpp
Utils/Http.cpp
Utils/Http.hpp
Utils/FixModelByWin10.cpp
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 8d50998c4..7309654a8 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -89,11 +89,13 @@ void BackgroundSlicingProcess::process_fff()
{
assert(m_print == m_fff_print);
m_print->process();
- wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
+ wxCommandEvent evt(m_event_slicing_completed_id);
+ evt.SetInt((int)(m_fff_print->step_state_with_timestamp(PrintStep::psBrim).timestamp));
+ wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
-
if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
+ wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
@@ -124,6 +126,7 @@ void BackgroundSlicingProcess::process_fff()
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());
} else if (! m_upload_job.empty()) {
+ wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
prepare_upload();
} else {
m_print->set_status(100, _utf8(L("Slicing complete")));
@@ -149,6 +152,8 @@ void BackgroundSlicingProcess::process_sla()
m_print->process();
if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
+ wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
+
const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
Zipper zipper(export_path);
@@ -170,6 +175,7 @@ void BackgroundSlicingProcess::process_sla()
m_print->set_status(100, (boost::format(_utf8(L("Masked SLA file exported to %1%"))) % export_path).str());
} else if (! m_upload_job.empty()) {
+ wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
prepare_upload();
} else {
m_print->set_status(100, _utf8(L("Slicing complete")));
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index 38e9e1075..c4672f1b4 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -60,6 +60,10 @@ public:
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code export is finished.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_finished_event(int event_id) { m_event_finished_id = event_id; }
+ // The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code is being exported to
+ // specified path or uploaded.
+ // The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
+ void set_export_began_event(int event_id) { m_event_export_began_id = event_id; }
// Activate either m_fff_print or m_sla_print.
// Return true if changed.
@@ -190,6 +194,9 @@ private:
int m_event_slicing_completed_id = 0;
// wxWidgets command ID to be sent to the plater to inform that the task finished.
int m_event_finished_id = 0;
+ // wxWidgets command ID to be sent to the plater to inform that the G-code is being exported.
+ int m_event_export_began_id = 0;
+
};
}; // namespace Slic3r
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 5109d2426..1edd7aa2b 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -31,6 +31,7 @@
#include "GUI_ObjectManipulation.hpp"
#include "Mouse3DController.hpp"
#include "I18N.hpp"
+#include "NotificationManager.hpp"
#if ENABLE_RETINA_GL
#include "slic3r/Utils/RetinaHelper.hpp"
@@ -651,19 +652,45 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
m_warnings.emplace_back(warning);
std::sort(m_warnings.begin(), m_warnings.end());
+
+ std::string text;
+ switch (warning) {
+ case ObjectOutside: text = L("An object outside the print area was detected."); break;
+ case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
+ case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
+ case SomethingNotShown: text = L("Some objects are not visible."); break;
+ case ObjectClashed: wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(L("An object outside the print area was detected.\n"
+ "Resolve the current problem to continue slicing."),
+ *(wxGetApp().plater()->get_current_canvas3D()));
+ break;
+ }
+ if (!text.empty())
+ wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D()));
}
else {
if (it == m_warnings.end()) // deactivating something that is not active is an easy task
return;
m_warnings.erase(it);
- if (m_warnings.empty()) { // nothing remains to be shown
+
+ std::string text;
+ switch (warning) {
+ case ObjectOutside: text = L("An object outside the print area was detected."); break;
+ case ToolpathOutside: text = L("A toolpath outside the print area was detected."); break;
+ case SlaSupportsOutside: text = L("SLA supports outside the print area were detected."); break;
+ case SomethingNotShown: text = L("Some objects are not visibl.e"); break;
+ case ObjectClashed: wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(); break;
+ }
+ if (!text.empty())
+ wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text);
+
+ /*if (m_warnings.empty()) { // nothing remains to be shown
reset();
m_msg_text = "";// save information for rescaling
return;
- }
+ }*/
}
-
+ /*
// Look at the end of our vector and generate proper texture.
std::string text;
bool red_colored = false;
@@ -685,6 +712,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
// save information for rescaling
m_msg_text = text;
m_is_colored_red = red_colored;
+ */
}
@@ -2074,6 +2102,8 @@ void GLCanvas3D::render()
std::string tooltip;
+
+
// Negative coordinate means out of the window, likely because the window was deactivated.
// In that case the tooltip should be hidden.
if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.)
@@ -2103,6 +2133,8 @@ 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(*this);
wxGetApp().imgui()->render();
@@ -3418,6 +3450,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
#ifdef SLIC3R_DEBUG_MOUSE_EVENTS
printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str());
#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */
+ m_dirty = true;
// do not return if dragging or tooltip not empty to allow for tooltip update
if (!m_mouse.dragging && m_tooltip.is_empty())
return;
@@ -3811,7 +3844,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_gizmos.reset_all_states();
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
- if (m_picking_enabled)
+ //if (m_picking_enabled)
m_dirty = true;
}
else
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index bfb158619..49d08565a 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -54,6 +54,7 @@
#include "Mouse3DController.hpp"
#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
+#include "NotificationManager.hpp"
#ifdef __WXMSW__
#include
@@ -384,7 +385,7 @@ bool GUI_App::on_init_inner()
// supplied as argument to --datadir; in that case we should still run the wizard
preset_bundle->setup_directories();
-#ifdef __WXMSW__
+#ifdef __WXMSW__
associate_3mf_files();
#endif // __WXMSW__
@@ -392,6 +393,11 @@ bool GUI_App::on_init_inner()
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) {
app_config->set("version_online", into_u8(evt.GetString()));
app_config->save();
+ if(this->plater_ != nullptr) {
+ if (*Semver::parse(SLIC3R_VERSION) < * Semver::parse(into_u8(evt.GetString()))) {
+ this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAviable, *(this->plater_->get_current_canvas3D()));
+ }
+ }
});
// initialize label colors and fonts
@@ -1439,7 +1445,7 @@ void GUI_App::check_updates(const bool verbose)
PresetUpdater::UpdateResult updater_result;
try {
- updater_result = preset_updater->config_update(app_config->orig_version());
+ updater_result = preset_updater->config_update(app_config->orig_version(), verbose);
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
mainframe->Close();
}
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index c2b257f45..922d6173d 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -194,12 +194,15 @@ public:
Plater* plater();
Model& model();
+
AppConfig* app_config{ nullptr };
PresetBundle* preset_bundle{ nullptr };
PresetUpdater* preset_updater{ nullptr };
MainFrame* mainframe{ nullptr };
Plater* plater_{ nullptr };
+ PresetUpdater* get_preset_updater() { return preset_updater; }
+
wxNotebook* tab_panel() const ;
int extruders_cnt() const;
int extruders_edited_cnt() const;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index a21194d94..266472eca 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -37,11 +37,17 @@ namespace GUI {
static const std::map font_icons = {
- {ImGui::PrintIconMarker , "cog" },
- {ImGui::PrinterIconMarker , "printer" },
- {ImGui::PrinterSlaIconMarker, "sla_printer"},
- {ImGui::FilamentIconMarker , "spool" },
- {ImGui::MaterialIconMarker , "resin" }
+ {ImGui::PrintIconMarker , "cog" },
+ {ImGui::PrinterIconMarker , "printer" },
+ {ImGui::PrinterSlaIconMarker, "sla_printer" },
+ {ImGui::FilamentIconMarker , "spool" },
+ {ImGui::MaterialIconMarker , "resin" },
+ {ImGui::CloseIconMarker , "cross" },
+ {ImGui::CloseIconHoverMarker, "cross_focus_large" },
+ {ImGui::TimerDotMarker , "timer_dot" },
+ {ImGui::TimerDotEmptyMarker , "timer_dot_empty" },
+ {ImGui::WarningMarker , "flag_green" },
+ {ImGui::ErrorMarker , "flag_red" }
};
ImGuiWrapper::ImGuiWrapper()
@@ -265,6 +271,11 @@ void ImGuiWrapper::set_next_window_bg_alpha(float alpha)
ImGui::SetNextWindowBgAlpha(alpha);
}
+void ImGuiWrapper::set_next_window_size(float x, float y, ImGuiCond cond)
+{
+ ImGui::SetNextWindowSize(ImVec2(x, y), cond);
+}
+
bool ImGuiWrapper::begin(const std::string &name, int flags)
{
return ImGui::Begin(name.c_str(), nullptr, (ImGuiWindowFlags)flags);
@@ -296,12 +307,23 @@ bool ImGuiWrapper::button(const wxString &label)
return ImGui::Button(label_utf8.c_str());
}
+bool ImGuiWrapper::button(const wxString& label, float width, float height)
+{
+ auto label_utf8 = into_u8(label);
+ return ImGui::Button(label_utf8.c_str(), ImVec2(width, height));
+}
+
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
{
auto label_utf8 = into_u8(label);
return ImGui::RadioButton(label_utf8.c_str(), active);
}
+bool ImGuiWrapper::image_button()
+{
+ return false;
+}
+
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
{
return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str());
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index dc62e57a0..ee553c4b6 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -57,6 +57,7 @@ public:
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
void set_next_window_bg_alpha(float alpha);
+ void set_next_window_size(float x, float y, ImGuiCond cond);
bool begin(const std::string &name, int flags = 0);
bool begin(const wxString &name, int flags = 0);
@@ -65,7 +66,9 @@ public:
void end();
bool button(const wxString &label);
+ bool button(const wxString& label, float width, float height);
bool radio_button(const wxString &label, bool active);
+ bool image_button();
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index ec7cd8d45..33f0d6379 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -7,6 +7,7 @@
#include "AppConfig.hpp"
#include "GLCanvas3D.hpp"
#include "Plater.hpp"
+#include "NotificationManager.hpp"
#include
@@ -403,6 +404,8 @@ void Mouse3DController::disconnected()
m_params_by_device[m_device_str] = m_params_ui;
m_device_str.clear();
m_connected = false;
+ wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::Mouse3dDisconnected, *(wxGetApp().plater()->get_current_canvas3D()));
+
wxGetApp().plater()->CallAfter([]() {
Plater *plater = wxGetApp().plater();
if (plater != nullptr) {
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
new file mode 100644
index 000000000..b7301f3d8
--- /dev/null
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -0,0 +1,918 @@
+#include "NotificationManager.hpp"
+
+#include "GUI_App.hpp"
+#include "Plater.hpp"
+#include "GLCanvas3D.hpp"
+#include "ImGuiWrapper.hpp"
+
+#include "wxExtensions.hpp"
+
+#include
+#include
+#include
+#include
+
+
+
+
+#define NOTIFICATION_MAX_MOVE 3.0f
+
+#define GAP_WIDTH 10.0f
+#define SPACE_RIGHT_PANEL 10.0f
+
+namespace Slic3r {
+namespace GUI {
+
+wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent);
+wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
+wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent);
+
+namespace Notifications_Internal{
+ void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
+ {
+ if (fading_out)
+ ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
+ else
+ ImGui::PushStyleColor(idx, col);
+ }
+}
+//ScalableBitmap bmp_icon;
+//------PopNotification--------
+NotificationManager::PopNotification::PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler) :
+ m_data (n)
+ , m_id (id)
+ , 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)
+{
+ init();
+}
+NotificationManager::PopNotification::~PopNotification()
+{
+}
+NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y)
+{
+ if (m_finished)
+ return RenderResult::Finished;
+ if (m_close_pending) {
+ // request of extra frame will be done in caller function by ret val ClosePending
+ m_finished = true;
+ return RenderResult::ClosePending;
+ }
+ if (m_hidden) {
+ m_top_y = initial_y - GAP_WIDTH;
+ return RenderResult::Static;
+ }
+ RenderResult ret_val = m_counting_down ? RenderResult::Countdown : RenderResult::Static;
+ Size cnv_size = canvas.get_canvas_size();
+ ImGuiWrapper& imgui = *wxGetApp().imgui();
+ bool shown = true;
+ std::string name;
+ ImVec2 mouse_pos = ImGui::GetMousePos();
+
+ 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;
+ //top right position
+ ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - SPACE_RIGHT_PANEL, 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
+ 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();
+ ret_val = RenderResult::Hovered;
+ //reset fading
+ m_fading_out = false;
+ m_current_fade_opacity = 1.f;
+ m_remaining_time = m_data.duration;
+ m_countdown_frame = 0;
+ }
+
+ if (m_counting_down && m_remaining_time < 0)
+ m_close_pending = true;
+
+ if (m_close_pending) {
+ // request of extra frame will be done in caller function by ret val ClosePending
+ m_finished = true;
+ return RenderResult::ClosePending;
+ }
+
+ // color change based on fading out
+ bool fading_pop = false;
+ if (m_fading_out) {
+ if (!m_paused)
+ m_current_fade_opacity -= 1.f / ((m_fading_time + 1.f) * 60.f);
+ 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
+ for (size_t i = 0; i < m_id; i++)
+ name += " ";
+ if (imgui.begin(name, &shown, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar )) {
+ if (shown) {
+
+ 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());
+ */
+ if(m_counting_down)
+ render_countdown(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
+ 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);
+ if (m_multiline && m_lines_count > 3)
+ render_minimize_button(imgui, win_pos.x, win_pos.y);
+ } else {
+ // the user clicked on the [X] button ( ImGuiWindowFlags_NoTitleBar means theres no [X] button)
+ m_close_pending = true;
+ canvas.set_as_dirty();
+ }
+ }
+ imgui.end();
+
+ if (fading_pop) {
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ }
+ if (m_is_gray)
+ ImGui::PopStyleColor();
+ else if (m_data.level == NotificationLevel::ErrorNotification)
+ ImGui::PopStyleColor();
+ else if (m_data.level == NotificationLevel::WarningNotification)
+ ImGui::PopStyleColor();
+ return ret_val;
+}
+void NotificationManager::PopNotification::init()
+{
+ std::string text = m_text1 + " " + m_hypertext;
+ int last_end = 0;
+ m_lines_count = 0;
+
+ //determine line width
+ m_line_height = ImGui::CalcTextSize("A").y;
+
+ m_left_indentation = m_line_height;
+ if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
+ std::string text;
+ text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
+ float picture_width = ImGui::CalcTextSize(text.c_str()).x;
+ m_left_indentation = picture_width + m_line_height / 2;
+ }
+ m_window_width_offset = m_left_indentation + m_line_height * 2;
+ m_window_width = m_line_height * 25;
+
+ // count lines
+ m_endlines.clear();
+ while (last_end < text.length() - 1)
+ {
+ int next_hard_end = text.find_first_of('\n', last_end);
+ if (next_hard_end > 0 && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
+ //next line is ended by '/n'
+ m_endlines.push_back(next_hard_end);
+ last_end = next_hard_end + 1;
+ }
+ else {
+ // find next suitable endline
+ if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - 3.5f * m_line_height) {// m_window_width_offset) {
+ // more than one line till end
+ int next_space = text.find_first_of(' ', last_end);
+ if (next_space > 0) {
+ int next_space_candidate = text.find_first_of(' ', next_space + 1);
+ while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
+ next_space = next_space_candidate;
+ next_space_candidate = text.find_first_of(' ', next_space + 1);
+ }
+ m_endlines.push_back(next_space);
+ last_end = next_space + 1;
+ }
+ }
+ else {
+ m_endlines.push_back(text.length());
+ last_end = text.length();
+ }
+
+ }
+ m_lines_count++;
+ }
+}
+void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
+{
+ if (m_multiline) {
+ m_window_height = m_lines_count * m_line_height;
+ }else
+ {
+ m_window_height = 2 * m_line_height;
+ }
+ m_window_height += 1 * m_line_height; // top and bottom
+}
+
+void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
+{
+ ImVec2 win_size(win_size_x, win_size_y);
+ ImVec2 win_pos(win_pos_x, win_pos_y);
+ float x_offset = m_left_indentation;
+ std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
+ ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
+ // text posistions are calculated by lines count
+ // large texts has "more" button or are displayed whole
+ // smaller texts are divided as one liners and two liners
+ if (m_lines_count > 2) {
+ if (m_multiline) {
+
+ int last_end = 0;
+ float starting_y = m_line_height/2;//10;
+ float shift_y = m_line_height;// -m_line_height / 20;
+ for (size_t i = 0; i < m_lines_count; i++) {
+ std::string line = m_text1.substr(last_end , m_endlines[i] - last_end);
+ last_end = m_endlines[i] + 1;
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(starting_y + i * shift_y);
+ imgui.text(line.c_str());
+ }
+ //hyperlink text
+ if (!m_hypertext.empty())
+ {
+ render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(m_endlines[m_lines_count - 2] + 1, m_endlines[m_lines_count - 1] - m_endlines[m_lines_count - 2] - 1).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
+ }
+
+
+ } else {
+ // line1
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
+ imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
+ // line2
+ std::string line = m_text1.substr(m_endlines[0] + 1, m_endlines[1] - m_endlines[0] - 1);
+ if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x)
+ {
+ line = line.substr(0, line.length() - 6);
+ line += "..";
+ }else
+ line += " ";
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
+ imgui.text(line.c_str());
+ // "More" hypertext
+ render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
+ }
+ } else {
+ //text 1
+ float cursor_y = win_size.y / 2 - text_size.y / 2;
+ float cursor_x = x_offset;
+ if(m_lines_count > 1) {
+ // line1
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
+ imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
+ // line2
+ std::string line = m_text1.substr(m_endlines[0] + 1);
+ cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(cursor_y);
+ imgui.text(line.c_str());
+ cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
+ } else {
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(cursor_y);
+ imgui.text(m_text1.c_str());
+ cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
+ }
+ //hyperlink text
+ if (!m_hypertext.empty())
+ {
+ render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
+ }
+
+ //notification text 2
+ //text 2 is suposed to be after the hyperlink - currently it is not used
+ /*
+ if (!m_text2.empty())
+ {
+ ImVec2 part_size = ImGui::CalcTextSize(m_hypertext.c_str());
+ ImGui::SetCursorPosX(win_size.x / 2 + text_size.x / 2 - part_size.x + 8 - x_offset);
+ ImGui::SetCursorPosY(cursor_y);
+ imgui.text(m_text2.c_str());
+ }
+ */
+ }
+}
+
+void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more)
+{
+ //invisible button
+ ImVec2 part_size = ImGui::CalcTextSize(text.c_str());
+ ImGui::SetCursorPosX(text_x -4);
+ ImGui::SetCursorPosY(text_y -5);
+ ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
+ if (imgui.button(" ", part_size.x + 6, part_size.y + 10))
+ {
+ if (more)
+ {
+ m_multiline = true;
+ set_next_window_size(imgui);
+ }
+ else {
+ on_text_click();
+ m_close_pending = true;
+ }
+ }
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+
+ //hover color
+ ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
+ 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);
+ ImGui::SetCursorPosX(text_x);
+ ImGui::SetCursorPosY(text_y);
+ imgui.text(text.c_str());
+ ImGui::PopStyleColor();
+
+ //underline
+ ImVec2 lineEnd = ImGui::GetItemRectMax();
+ 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))));
+
+}
+
+void NotificationManager::PopNotification::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)
+{
+ ImVec2 win_size(win_size_x, win_size_y);
+ ImVec2 win_pos(win_pos_x, 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_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);
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
+
+
+ //button - if part if treggered
+ std::string button_text;
+ button_text = ImGui::CloseIconMarker;
+
+ if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
+ ImVec2(win_pos.x, win_pos.y + win_size.y - (m_multiline? 2 * m_line_height : 0)),
+ true))
+ {
+ button_text = ImGui::CloseIconHoverMarker;
+ }
+ ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
+ ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
+ ImGui::SetCursorPosX(win_size.x - m_line_height * 2.25f);
+ ImGui::SetCursorPosY(win_size.y / 2 - button_size.y/2);
+ if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
+ {
+ m_close_pending = true;
+ }
+
+ //invisible large button
+ ImGui::SetCursorPosX(win_size.x - win_size.x / 10.f);
+ ImGui::SetCursorPosY(0);
+ if (imgui.button(" ", win_size.x / 10.f, win_size.y - (m_multiline ? 2 * m_line_height : 0)))
+ {
+ m_close_pending = true;
+ }
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+}
+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)
+{
+ /*
+ ImVec2 win_size(win_size_x, win_size_y);
+ ImVec2 win_pos(win_pos_x, win_pos_y);
+
+ //countdown dots
+ std::string dot_text;
+ dot_text = m_remaining_time <= (float)m_data.duration / 4 * 3 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
+ ImGui::SetCursorPosX(win_size.x - m_line_height);
+ //ImGui::SetCursorPosY(win_size.y / 2 - 24);
+ ImGui::SetCursorPosY(0);
+ imgui.text(dot_text.c_str());
+
+ dot_text = m_remaining_time < m_data.duration / 2 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
+ ImGui::SetCursorPosX(win_size.x - m_line_height);
+ //ImGui::SetCursorPosY(win_size.y / 2 - 9);
+ ImGui::SetCursorPosY(win_size.y / 2 - m_line_height / 2);
+ imgui.text(dot_text.c_str());
+
+ dot_text = m_remaining_time <= m_data.duration / 4 ? ImGui::TimerDotEmptyMarker : ImGui::TimerDotMarker;
+ ImGui::SetCursorPosX(win_size.x - m_line_height);
+ //ImGui::SetCursorPosY(win_size.y / 2 + 6);
+ ImGui::SetCursorPosY(win_size.y - m_line_height);
+ imgui.text(dot_text.c_str());
+ */
+ if (!m_fading_out && m_remaining_time <= m_data.duration / 4) {
+ m_fading_out = true;
+ m_fading_time = m_remaining_time;
+ }
+
+ if (m_last_remaining_time != m_remaining_time) {
+ m_last_remaining_time = m_remaining_time;
+ m_countdown_frame = 0;
+ }
+ /*
+ //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++;
+ */
+}
+void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
+{
+ if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
+ std::string text;
+ text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
+ ImGui::SetCursorPosX(m_line_height / 3);
+ ImGui::SetCursorPosY(m_window_height / 2 - m_line_height / 2);
+ imgui.text(text.c_str());
+ }
+}
+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);
+ 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);
+
+
+ //button - if part if treggered
+ std::string button_text;
+ button_text = ImGui::CloseIconMarker;
+ if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1),
+ ImVec2(win_pos_x, win_pos_y + m_window_height),
+ true))
+ {
+ button_text = ImGui::CloseIconHoverMarker;
+ }
+ ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
+ ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
+ ImGui::SetCursorPosX(m_window_width - m_line_height * 2.25f);
+ ImGui::SetCursorPosY(m_window_height - button_size.y - 5);
+ if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
+ {
+ m_multiline = false;
+ }
+
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+ ImGui::PopStyleColor();
+}
+void NotificationManager::PopNotification::on_text_click()
+{
+ switch (m_data.type) {
+ case NotificationType::ExportToRemovableFinished :
+ assert(m_evt_handler != nullptr);
+ if (m_evt_handler != nullptr)
+ wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
+ break;
+ 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::PresetUpdateAviable :
+ //wxGetApp().plater()->export_gcode(false);
+ assert(m_evt_handler != nullptr);
+ if (m_evt_handler != nullptr)
+ wxPostEvent(m_evt_handler, PresetUpdateAviableClickedEvent(EVT_PRESET_UPDATE_AVIABLE_CLICKED));
+ break;
+ case NotificationType::NewAppAviable:
+ wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases");
+ break;
+ default:
+ break;
+ }
+}
+void NotificationManager::PopNotification::update(const NotificationData& n)
+{
+ m_text1 = n.text1;
+ m_hypertext = n.hypertext;
+ m_text2 = n.text2;
+ init();
+}
+bool NotificationManager::PopNotification::compare_text(const std::string& text)
+{
+ std::string t1(m_text1);
+ std::string t2(text);
+ t1.erase(std::remove_if(t1.begin(), t1.end(), ::isspace), t1.end());
+ t2.erase(std::remove_if(t2.begin(), t2.end(), ::isspace), t2.end());
+ if (t1.compare(t2) == 0)
+ return true;
+ return false;
+}
+
+NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool large) :
+ NotificationManager::PopNotification(n, id, evt_handler)
+{
+ set_large(large);
+}
+void NotificationManager::SlicingCompleteLargeNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
+{
+ if (!m_is_large)
+ PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
+ else {
+ ImVec2 win_size(win_size_x, win_size_y);
+ ImVec2 win_pos(win_pos_x, win_pos_y);
+
+ ImVec2 text1_size = ImGui::CalcTextSize(m_text1.c_str());
+ float x_offset = m_left_indentation;
+ std::string fulltext = m_text1 + m_hypertext + m_text2;
+ ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
+ float cursor_y = win_size.y / 2 - text_size.y / 2;
+ if (m_has_print_info) {
+ x_offset = 20;
+ cursor_y = win_size.y / 2 + win_size.y / 6 - text_size.y / 2;
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(cursor_y);
+ imgui.text(m_print_info.c_str());
+ cursor_y = win_size.y / 2 - win_size.y / 6 - text_size.y / 2;
+ }
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(cursor_y);
+ imgui.text(m_text1.c_str());
+
+ render_hypertext(imgui, x_offset + text1_size.x + 4, cursor_y, m_hypertext);
+
+ }
+}
+void NotificationManager::SlicingCompleteLargeNotification::set_print_info(std::string info)
+{
+ m_print_info = info;
+ m_has_print_info = true;
+ if(m_is_large)
+ m_lines_count = 2;
+}
+void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
+{
+ m_is_large = l;
+ m_counting_down = !l;
+ m_hypertext = l ? _u8L("Export G-Code.") : std::string();
+ m_hidden = !l;
+}
+//------NotificationManager--------
+NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
+ m_evt_handler(evt_handler)
+{
+}
+NotificationManager::~NotificationManager()
+{
+ for (PopNotification* notification : m_pop_notifications)
+ {
+ delete notification;
+ }
+}
+void NotificationManager::push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp)
+{
+ auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(),
+ boost::bind(&NotificationData::type, _1) == type);
+ if (it != basic_notifications.end())
+ push_notification_data( *it, canvas, timestamp);
+}
+void NotificationManager::push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp)
+{
+ push_notification_data({ NotificationType::CustomNotification, NotificationLevel::RegularNotification, 10, text }, canvas, timestamp );
+}
+void NotificationManager::push_notification(const std::string& text, NotificationManager::NotificationLevel level, GLCanvas3D& canvas, int timestamp)
+{
+ switch (level)
+ {
+ case Slic3r::GUI::NotificationManager::NotificationLevel::RegularNotification:
+ push_notification_data({ NotificationType::CustomNotification, level, 10, text }, canvas, timestamp);
+ break;
+ case Slic3r::GUI::NotificationManager::NotificationLevel::ErrorNotification:
+ push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp);
+
+ break;
+ case Slic3r::GUI::NotificationManager::NotificationLevel::ImportantNotification:
+ push_notification_data({ NotificationType::CustomNotification, level, 0, text }, canvas, timestamp);
+ break;
+ default:
+ break;
+ }
+}
+void NotificationManager::push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas)
+{
+ set_all_slicing_errors_gray(false);
+ push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0);
+ close_notification_of_type(NotificationType::SlicingComplete);
+}
+void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step)
+{
+ NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
+
+ NotificationManager::SlicingWarningNotification* notification = new NotificationManager::SlicingWarningNotification(data, m_next_id++, m_evt_handler);
+ notification->set_object_id(oid);
+ notification->set_warning_step(warning_step);
+ if
+ (push_notification_data(notification, canvas, 0)) {
+ notification->set_gray(gray);
+ }
+ else {
+ delete notification;
+ }
+
+}
+void NotificationManager::push_plater_error_notification(const std::string& text, GLCanvas3D& canvas)
+{
+ push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, canvas, 0);
+}
+void NotificationManager::push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas)
+{
+ push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, canvas, 0);
+}
+void NotificationManager::close_plater_error_notification()
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::PlaterError) {
+ notification->close();
+ }
+ }
+}
+void NotificationManager::close_plater_warning_notification(const std::string& text)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
+ notification->close();
+ }
+ }
+}
+void NotificationManager::set_all_slicing_errors_gray(bool g)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingError) {
+ notification->set_gray(g);
+ }
+ }
+}
+void NotificationManager::set_all_slicing_warnings_gray(bool g)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingWarning) {
+ notification->set_gray(g);
+ }
+ }
+}
+void NotificationManager::set_slicing_warning_gray(const std::string& text, bool g)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingWarning && notification->compare_text(text)) {
+ notification->set_gray(g);
+ }
+ }
+}
+void NotificationManager::close_slicing_errors_and_warnings()
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingError || notification->get_type() == NotificationType::SlicingWarning) {
+ notification->close();
+ }
+ }
+}
+void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large)
+{
+ std::string hypertext;
+ int time = 10;
+ if(large)
+ {
+ hypertext = _u8L("Export G-Code.");
+ time = 0;
+ }
+ NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext };
+
+ NotificationManager::SlicingCompleteLargeNotification* notification = new NotificationManager::SlicingCompleteLargeNotification(data, m_next_id++, m_evt_handler, large);
+ if (push_notification_data(notification, canvas, timestamp)) {
+ } else {
+ delete notification;
+ }
+}
+void NotificationManager::set_slicing_complete_print_time(std::string info)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingComplete) {
+ dynamic_cast(notification)->set_print_info(info);
+ break;
+ }
+ }
+}
+void NotificationManager::set_slicing_complete_large(bool large)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingComplete) {
+ dynamic_cast(notification)->set_large(large);
+ break;
+ }
+ }
+}
+void NotificationManager::close_notification_of_type(const NotificationType type)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == type) {
+ notification->close();
+ }
+ }
+}
+void NotificationManager::compare_warning_oids(const std::vector& living_oids)
+{
+ for (PopNotification* notification : m_pop_notifications) {
+ if (notification->get_type() == NotificationType::SlicingWarning) {
+ auto w = dynamic_cast(notification);
+ bool found = false;
+ for (size_t oid : living_oids) {
+ if (w->get_object_id() == oid) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ notification->close();
+ }
+ }
+}
+bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp)
+{
+ PopNotification* n = new PopNotification(notification_data, m_next_id++, m_evt_handler);
+ bool r = push_notification_data(n, canvas, timestamp);
+ if (!r)
+ delete n;
+ return r;
+}
+bool NotificationManager::push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp)
+{
+ // if timestamped notif, push only new one
+ if (timestamp != 0) {
+ if (m_used_timestamps.find(timestamp) == m_used_timestamps.end()) {
+ m_used_timestamps.insert(timestamp);
+ } else {
+ return false;
+ }
+ }
+ if (!this->find_older(notification)) {
+ m_pop_notifications.emplace_back(notification);
+ canvas.request_extra_frame();
+ return true;
+ } else {
+ m_pop_notifications.back()->update(notification->get_data());
+ canvas.request_extra_frame();
+ return false;
+ }
+}
+void NotificationManager::render_notifications(GLCanvas3D& canvas)
+{
+ float last_x = 0.0f;
+ float current_height = 0.0f;
+ bool request_next_frame = false;
+ bool render_main = false;
+ bool hovered = false;
+ sort_notifications();
+ // iterate thru notifications and render them / erease them
+ for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
+ if ((*it)->get_finished()) {
+ delete (*it);
+ it = m_pop_notifications.erase(it);
+ } else {
+ (*it)->set_paused(m_hovered);
+ PopNotification::RenderResult res = (*it)->render(canvas, last_x);
+ if (res != PopNotification::RenderResult::Finished) {
+ last_x = (*it)->get_top() + GAP_WIDTH;
+ current_height = std::max(current_height, (*it)->get_current_top());
+ render_main = true;
+ }
+ if (res == PopNotification::RenderResult::Countdown || res == PopNotification::RenderResult::ClosePending || res == PopNotification::RenderResult::Finished)
+ request_next_frame = true;
+ if (res == PopNotification::RenderResult::Hovered)
+ hovered = true;
+ ++it;
+ }
+ }
+ m_hovered = hovered;
+
+ //actualizate timers and request frame if needed
+ wxWindow* p = dynamic_cast (wxGetApp().plater());
+ while (p->GetParent())
+ p = p->GetParent();
+ wxTopLevelWindow* top_level_wnd = dynamic_cast(p);
+ if (!top_level_wnd->IsActive())
+ return;
+
+ if (!m_hovered && m_last_time < wxGetLocalTime())
+ {
+ if (wxGetLocalTime() - m_last_time == 1)
+ {
+ for(auto notification : m_pop_notifications)
+ {
+ notification->substract_remaining_time();
+ }
+ }
+ m_last_time = wxGetLocalTime();
+ }
+
+ if (request_next_frame)
+ canvas.request_extra_frame();
+}
+
+
+void NotificationManager::sort_notifications()
+{
+ std::sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](PopNotification* n1, PopNotification* 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())
+ return true;
+ return (n1l < n2l);
+ });
+}
+
+bool NotificationManager::find_older(NotificationManager::PopNotification* notification)
+{
+ NotificationType type = notification->get_type();
+ std::string text = notification->get_data().text1;
+ for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) {
+ if((*it)->get_type() == type && !(*it)->get_finished()) {
+ if (type == NotificationType::CustomNotification || type == NotificationType::PlaterWarning) {
+ if (!(*it)->compare_text(text))
+ continue;
+ }else if (type == NotificationType::SlicingWarning) {
+ auto w1 = dynamic_cast(notification);
+ auto w2 = dynamic_cast(*it);
+ if (w1 != nullptr && w2 != nullptr) {
+ if (!(*it)->compare_text(text) || w1->get_object_id() != w2->get_object_id()) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+
+ if (it != m_pop_notifications.end() - 1)
+ std::rotate(it, it + 1, m_pop_notifications.end());
+ return true;
+ }
+ }
+ return false;
+}
+
+void NotificationManager::dpi_changed()
+{
+
+}
+
+}//namespace GUI
+}//namespace Slic3r
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
new file mode 100644
index 000000000..d7037c53e
--- /dev/null
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -0,0 +1,268 @@
+#ifndef slic3r_GUI_NotificationManager_hpp_
+#define slic3r_GUI_NotificationManager_hpp_
+
+#include "Event.hpp"
+#include "I18N.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace Slic3r {
+namespace GUI {
+
+using EjectDriveNotificationClickedEvent = SimpleEvent;
+wxDECLARE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent);
+using ExportGcodeNotificationClickedEvent = SimpleEvent;
+wxDECLARE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
+using PresetUpdateAviableClickedEvent = SimpleEvent;
+wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVIABLE_CLICKED, PresetUpdateAviableClickedEvent);
+
+class GLCanvas3D;
+class ImGuiWrapper;
+
+enum class NotificationType
+{
+ CustomNotification,
+ SlicingComplete,
+ SlicingNotPossible,
+ ExportToRemovableFinished,
+ Mouse3dDisconnected,
+ Mouse3dConnected,
+ NewPresetsAviable,
+ NewAppAviable,
+ PresetUpdateAviable,
+ LoadingFailed,
+ ValidateError, // currently not used - instead Slicing error is used for both slicing and validate errors
+ SlicingError,
+ SlicingWarning,
+ PlaterError,
+ PlaterWarning,
+ ApplyError
+
+};
+class NotificationManager
+{
+public:
+ enum class NotificationLevel : int
+ {
+ ErrorNotification = 4,
+ WarningNotification = 3,
+ ImportantNotification = 2,
+ RegularNotification = 1,
+ };
+ // duration 0 means not disapearing
+ struct NotificationData {
+ NotificationType type;
+ NotificationLevel level;
+ const int duration;
+ const std::string text1;
+ const std::string hypertext = std::string();
+ const std::string text2 = std::string();
+ };
+
+ //Pop notification - shows only once to user.
+ class PopNotification
+ {
+ public:
+ enum class RenderResult
+ {
+ Finished,
+ ClosePending,
+ Static,
+ Countdown,
+ Hovered
+ };
+ PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler);
+ virtual ~PopNotification();
+ RenderResult render(GLCanvas3D& canvas, const float& initial_y);
+ // 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; }
+ // 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; }
+ // 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);
+ protected:
+ // Call after every size change
+ void init();
+ // Calculetes correct size but not se it in imgui!
+ virtual void set_next_window_size(ImGuiWrapper& imgui);
+ 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_close_button(ImGuiWrapper& imgui,
+ const float win_size_x, const float win_size_y,
+ const float win_pos_x , const float win_pos_y);
+ 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);
+ void render_hypertext(ImGuiWrapper& imgui,
+ const float text_x, const float text_y,
+ const std::string text,
+ bool more = false);
+ void render_left_sign(ImGuiWrapper& imgui);
+ void render_minimize_button(ImGuiWrapper& imgui,
+ const float win_pos_x, const float win_pos_y);
+ void on_text_click();
+
+ const NotificationData m_data;
+
+ int m_id;
+ // 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 };
+ // total time left when fading beggins
+ float m_fading_time{ 0.0f };
+ float m_current_fade_opacity{ 1.f };
+ // 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 };
+ // variables to count positions correctly
+ float m_window_width_offset;
+ float m_left_indentation;
+ // Total size of notification window - varies based on monitor
+ float m_window_height { 56.0f };
+ 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!
+ float m_line_height;
+ std::vector m_endlines;
+ // Gray are f.e. eorrors when its uknown if they are still valid
+ bool m_is_gray { false };
+ //if multiline = true, notification is showing all lines(>2)
+ bool m_multiline { false };
+ int m_lines_count{ 1 };
+ wxEvtHandler* m_evt_handler;
+ };
+
+ class SlicingCompleteLargeNotification : public PopNotification
+ {
+ public:
+ SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool largeds);
+ void set_large(bool l);
+ bool get_large() { return m_is_large; }
+
+ void set_print_info(std::string info);
+ protected:
+ 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)
+ override;
+
+ bool m_is_large;
+ bool m_has_print_info { false };
+ std::string m_print_info { std::string() };
+ };
+
+ class SlicingWarningNotification : public PopNotification
+ {
+ public:
+ SlicingWarningNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler) : PopNotification(n, id, 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; }
+ const int get_warning_step() { return warning_step; }
+ protected:
+ size_t object_id;
+ int warning_step;
+ };
+
+ NotificationManager(wxEvtHandler* evt_handler);
+ ~NotificationManager();
+
+
+ // only type means one of basic_notification (see below)
+ void push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp = 0);
+ // only text means Undefined type
+ void push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp = 0);
+ void push_notification(const std::string& text, NotificationLevel level, GLCanvas3D& canvas, int timestamp = 0);
+ // creates Slicing Error notification with custom text
+ void push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas);
+ // creates Slicing Warning notification with custom text
+ void push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, size_t oid, int warning_step);
+ // marks slicing errors as gray
+ void set_all_slicing_errors_gray(bool g);
+ // marks slicing warings as gray
+ void set_all_slicing_warnings_gray(bool g);
+ void set_slicing_warning_gray(const std::string& text, bool g);
+ // imidietly stops showing slicing errors
+ void close_slicing_errors_and_warnings();
+ void compare_warning_oids(const std::vector& living_oids);
+ void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas);
+ void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas);
+ void close_plater_error_notification();
+ void close_plater_warning_notification(const std::string& text);
+ // creates special notification slicing complete
+ // if large = true prints printing time and export button
+ void push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large);
+ void set_slicing_complete_print_time(std::string info);
+ void set_slicing_complete_large(bool large);
+ // renders notifications in queue and deletes expired ones
+ void render_notifications(GLCanvas3D& canvas);
+ // finds and closes all notifications of given type
+ void close_notification_of_type(const NotificationType type);
+ void dpi_changed();
+private:
+ //pushes notification into the queue of notifications that are rendered
+ //can be used to create custom notification
+ bool push_notification_data(const NotificationData& notification_data, GLCanvas3D& canvas, int timestamp);
+ bool push_notification_data(NotificationManager::PopNotification* notification, GLCanvas3D& canvas, int timestamp);
+ //finds older notification of same type and moves it to the end of queue. returns true if found
+ bool find_older(NotificationManager::PopNotification* notification);
+ void sort_notifications();
+
+ wxEvtHandler* m_evt_handler;
+ 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;
+
+ //prepared (basic) notifications
+ const std::vector 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::PresetUpdateAviable, 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.") },
+ //{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
+ };
+};
+
+}//namespace GUI
+}//namespace Slic3r
+
+#endif //slic3r_GUI_NotificationManager_hpp_
\ No newline at end of file
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 761f574e1..9cfc717db 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -75,8 +75,10 @@
#include "../Utils/PrintHost.hpp"
#include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp"
+#include "../Utils/PresetUpdater.hpp"
#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
+#include "NotificationManager.hpp"
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@@ -102,6 +104,7 @@ wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent);
wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, wxCommandEvent);
+wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
// Sidebar widgets
@@ -716,7 +719,7 @@ struct Sidebar::priv
wxButton *btn_export_gcode;
wxButton *btn_reslice;
ScalableButton *btn_send_gcode;
- ScalableButton *btn_remove_device;
+ ScalableButton *btn_eject_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
bool is_collapsed {false};
@@ -889,12 +892,12 @@ Sidebar::Sidebar(Plater *parent)
};
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + "\tCtrl+Shift+G");
- init_scalable_btn(&p->btn_remove_device, "eject_sd" , _L("Remove device") + "\tCtrl+T");
+ init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device") + "\tCtrl+T");
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + "\tCtrl+U");
// regular buttons "Slice now" and "Export G-code"
- const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4;
+ const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4;
auto init_btn = [this](wxButton **btn, wxString label, const int button_height) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxSize(-1, button_height), wxBU_EXACTFIT);
@@ -912,7 +915,7 @@ Sidebar::Sidebar(Plater *parent)
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
complect_btns_sizer->Add(p->btn_export_gcode_removable);
- complect_btns_sizer->Add(p->btn_remove_device);
+ complect_btns_sizer->Add(p->btn_eject_device);
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
@@ -935,7 +938,7 @@ Sidebar::Sidebar(Plater *parent)
p->plater->select_view_3D("Preview");
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
- p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
+ p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
@@ -1083,9 +1086,9 @@ void Sidebar::msw_rescale()
p->object_info->msw_rescale();
p->btn_send_gcode->msw_rescale();
- p->btn_remove_device->msw_rescale();
+ p->btn_eject_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
- const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
+ const int scaled_height = p->btn_eject_device->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
@@ -1114,7 +1117,7 @@ void Sidebar::sys_color_changed()
// btn...->msw_rescale() updates icon on button, so use it
p->btn_send_gcode->msw_rescale();
- p->btn_remove_device->msw_rescale();
+ p->btn_eject_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
p->scrolled->Layout();
@@ -1350,6 +1353,12 @@ void Sidebar::update_sliced_info_sizer()
new_label += format_wxstr("\n - %1%", _L("normal mode"));
info_text += format_wxstr("\n%1%", ps.estimated_normal_print_time);
fill_labels(ps.estimated_normal_custom_gcode_print_times, new_label, info_text);
+
+ // uncomment next line to not disappear slicing finished notif when colapsing sidebar before time estimate
+ //if (p->plater->is_sidebar_collapsed())
+ p->plater->get_notification_manager()->set_slicing_complete_large(p->plater->is_sidebar_collapsed());
+ p->plater->get_notification_manager()->set_slicing_complete_print_time("Estimated printing time: " + ps.estimated_normal_print_time);
+
}
if (ps.estimated_silent_print_time != "N/A") {
new_label += format_wxstr("\n - %1%", _L("stealth mode"));
@@ -1385,15 +1394,16 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
- p->btn_remove_device->Enable(enable);
+ p->btn_eject_device->Enable(enable);
p->btn_export_gcode_removable->Enable(enable);
}
-bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
-bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
-bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
-bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); }
-bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
+bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
+bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
+bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
+bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); }
+bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); }
+bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); }
bool Sidebar::is_multifilament()
{
@@ -1591,6 +1601,7 @@ struct Plater::priv
GLToolbar view_toolbar;
GLToolbar collapse_toolbar;
Preview *preview;
+ NotificationManager* notification_manager;
BackgroundSlicingProcess background_process;
bool suppressed_backround_processing_update { false };
@@ -1775,7 +1786,17 @@ struct Plater::priv
void on_slicing_update(SlicingStatusEvent&);
void on_slicing_completed(wxCommandEvent&);
void on_process_completed(wxCommandEvent&);
+ void on_export_began(wxCommandEvent&);
void on_layer_editing_toggled(bool enable);
+ void on_slicing_began();
+
+ void clear_warnings();
+ void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid);
+ void actualizate_warnings(const Model& model, size_t print_oid);
+ // Displays dialog window with list of warnings.
+ // Returns true if user clicks OK.
+ // Returns true if current_warnings vector is empty without showning the dialog
+ bool warnings_dialog();
void on_action_add(SimpleEvent&);
void on_action_split_objects(SimpleEvent&);
@@ -1826,7 +1847,7 @@ struct Plater::priv
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
bool writing_to_removable_device = { false };
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
-
+ bool process_completed_with_error { false };
private:
bool init_object_menu();
bool init_common_menu(wxMenu* menu, const bool is_part = false);
@@ -1854,6 +1875,11 @@ private:
* */
std::string m_last_fff_printer_profile_name;
std::string m_last_sla_printer_profile_name;
+
+ // vector of all warnings generated by last slicing
+ std::vector> current_warnings;
+ bool show_warning_dialog { false };
+
};
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
@@ -1899,6 +1925,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
});
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
+ background_process.set_export_began_event(EVT_EXPORT_BEGAN);
// Default printer technology for default config.
background_process.select_technology(this->printer_technology);
// Register progress callback from the Print class to the Plater.
@@ -2010,8 +2037,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
- q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
+ q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
+ q->Bind(EVT_EXPORT_BEGAN, &priv::on_export_began, this);
q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); });
q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); });
@@ -2038,16 +2066,27 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
});
#endif /* _WIN32 */
- this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this](RemovableDriveEjectEvent &evt) {
+ notification_manager = new NotificationManager(this->q);
+ this->q->Bind(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, [this](EjectDriveNotificationClickedEvent&) { this->q->eject_drive(); });
+ this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); });
+ this->q->Bind(EVT_PRESET_UPDATE_AVIABLE_CLICKED, [this](PresetUpdateAviableClickedEvent&) { wxGetApp().get_preset_updater()->on_update_notification_confirm(); });
+
+ this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) {
if (evt.data.second) {
this->show_action_buttons(this->ready_to_slice);
- Slic3r::GUI::show_info(this->q, format_wxstr(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),
- evt.data.first.name, evt.data.first.path));
- } else
- Slic3r::GUI::show_info(this->q, format_wxstr(_L("Ejecting of device %s(%s) has failed."),
- evt.data.first.name, evt.data.first.path));
+ notification_manager->push_notification(format(_L("Unmounting successful. The device %s(%s) can now be safely removed from the computer."),evt.data.first.name, evt.data.first.path),
+ NotificationManager::NotificationLevel::RegularNotification, *q->get_current_canvas3D());
+ } else {
+ notification_manager->push_notification(format(_L("Ejecting of device %s(%s) has failed."), evt.data.first.name, evt.data.first.path),
+ NotificationManager::NotificationLevel::ErrorNotification, *q->get_current_canvas3D());
+ }
+ });
+ this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) {
+ this->show_action_buttons(this->ready_to_slice);
+ if (!this->sidebar->get_eject_shown()) {
+ notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished);
+ }
});
- this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this](RemovableDrivesChangedEvent &) { this->show_action_buttons(this->ready_to_slice); });
// Start the background thread and register this window as a target for update events.
wxGetApp().removable_drive_manager()->init(this->q);
#ifdef _WIN32
@@ -2675,6 +2714,8 @@ void Plater::priv::reset()
{
Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
+ clear_warnings();
+
set_project_filename(wxEmptyString);
// Prevent toolpaths preview from rendering while we modify the Print object
@@ -2844,22 +2885,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
std::string err = this->background_process.validate();
if (err.empty()) {
+ notification_manager->set_all_slicing_errors_gray(true);
if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled())
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
} else {
- // The print is not valid.
- // Only show the error message immediately, if the top level parent of this window is active.
- auto p = dynamic_cast(this->q);
- while (p->GetParent())
- p = p->GetParent();
- auto *top_level_wnd = dynamic_cast(p);
- if (! postpone_error_messages && top_level_wnd && top_level_wnd->IsActive()) {
- // The error returned from the Print needs to be translated into the local language.
- GUI::show_error(this->q, err);
- } else {
- // Show the error message once the main window gets activated.
- this->delayed_error_message = err;
- }
+ // The print is not valid.
+ // Show error as notification.
+ notification_manager->push_slicing_error_notification(err, *q->get_current_canvas3D());
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
}
} else if (! this->delayed_error_message.empty()) {
@@ -2867,6 +2899,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
}
+ //actualizate warnings
+ if (invalidated != Print::APPLY_STATUS_UNCHANGED) {
+ actualizate_warnings(this->q->model(), this->background_process.current_print()->id().id);
+ notification_manager->set_all_slicing_warnings_gray(true);
+ show_warning_dialog = false;
+ process_completed_with_error = false;
+ }
+
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
(return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) {
// The background processing was killed and it will not be restarted.
@@ -2929,6 +2969,8 @@ bool Plater::priv::restart_background_process(unsigned int state)
this->statusbar()->set_status_text(_L("Cancelling"));
this->background_process.stop();
});
+ if (!show_warning_dialog)
+ on_slicing_began();
return true;
}
}
@@ -2955,6 +2997,7 @@ void Plater::priv::export_gcode(fs::path output_path, bool output_path_on_remova
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0)
return;
+ show_warning_dialog = true;
if (! output_path.empty()) {
background_process.schedule_export(output_path.string(), output_path_on_removable_media);
} else {
@@ -3433,11 +3476,20 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
state = print_object->step_state_with_warnings(static_cast(warning_step));
}
// Now process state.warnings.
+ for (auto const& warning : state.warnings) {
+ if (warning.current) {
+ notification_manager->push_slicing_warning_notification(warning.message, false, *q->get_current_canvas3D(), object_id.id, warning_step);
+ add_warning(warning, object_id.id);
+ }
+ }
}
}
-void Plater::priv::on_slicing_completed(wxCommandEvent &)
+void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
{
+ //notification_manager->push_notification(NotificationType::SlicingComplete, *q->get_current_canvas3D(), evt.GetInt());
+ 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();
@@ -3450,8 +3502,63 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &)
break;
default: break;
}
-}
+}
+void Plater::priv::on_export_began(wxCommandEvent& evt)
+{
+ if (show_warning_dialog)
+ warnings_dialog();
+}
+void Plater::priv::on_slicing_began()
+{
+ clear_warnings();
+ notification_manager->close_notification_of_type(NotificationType::SlicingComplete);
+}
+void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
+{
+ for (auto const& it : current_warnings) {
+ if (warning.message_id == it.first.message_id) {
+ if (warning.message_id != 0 || (warning.message_id == 0 && warning.message == it.first.message))
+ return;
+ }
+ }
+ current_warnings.emplace_back(std::pair(warning, oid));
+}
+void Plater::priv::actualizate_warnings(const Model& model, size_t print_oid)
+{
+ std::vector living_oids;
+ living_oids.push_back(model.id().id);
+ living_oids.push_back(print_oid);
+ for (auto it = model.objects.begin(); it != model.objects.end(); ++it) {
+ living_oids.push_back((*it)->id().id);
+ }
+ notification_manager->compare_warning_oids(living_oids);
+}
+void Plater::priv::clear_warnings()
+{
+ notification_manager->close_slicing_errors_and_warnings();
+ this->current_warnings.clear();
+}
+bool Plater::priv::warnings_dialog()
+{
+ if (current_warnings.empty())
+ return true;
+ std::string text = _u8L("There are active warnings concerning sliced models:\n");
+ bool empt = true;
+ for (auto const& it : current_warnings) {
+ int next_n = it.first.message.find_first_of('\n', 0);
+ text += "\n";
+ if (next_n != std::string::npos)
+ text += it.first.message.substr(0, next_n);
+ else
+ text += it.first.message;
+ }
+ //text += "\n\nDo you still wish to export?";
+ wxMessageDialog msg_wingow(this->q, text, wxString(SLIC3R_APP_NAME " ") + _L("generated warnings"), wxOK);
+ const auto res = msg_wingow.ShowModal();
+ return res == wxID_OK;
+
+}
void Plater::priv::on_process_completed(wxCommandEvent &evt)
{
// Stop the background task, wait until the thread goes into the "Idle" state.
@@ -3470,14 +3577,13 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
if (error) {
wxString message = evt.GetString();
if (message.IsEmpty())
- message = _L("Export failed");
- if (q->m_tracking_popup_menu)
- // We don't want to pop-up a message box when tracking a pop-up menu.
- // We postpone the error message instead.
- q->m_tracking_popup_menu_error_message = message;
- else
- show_error(q, message);
+ message = _L("Export failed.");
+ notification_manager->push_slicing_error_notification(boost::nowide::narrow(message), *q->get_current_canvas3D());
this->statusbar()->set_status_text(message);
+ const wxString invalid_str = _L("Invalid data");
+ for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport })
+ sidebar->set_btn_label(btn, invalid_str);
+ process_completed_with_error = true;
}
if (canceled)
this->statusbar()->set_status_text(_L("Cancelled"));
@@ -3503,18 +3609,21 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
default: break;
}
-
if (canceled) {
if (wxGetApp().get_mode() == comSimple)
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
show_action_buttons(true);
}
- else if (this->writing_to_removable_device || wxGetApp().get_mode() == comSimple)
+ else if (wxGetApp().get_mode() == comSimple)
{
- wxGetApp().removable_drive_manager()->set_exporting_finished(true);
show_action_buttons(false);
}
- this->writing_to_removable_device = false;
+ else if (this->writing_to_removable_device)
+ {
+ show_action_buttons(false);
+ notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D());
+ }
+ this->writing_to_removable_device = false;
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@@ -4156,7 +4265,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
sidebar->show_export_removable(removable_media_status.has_removable_drives) |
- sidebar->show_disconnect(removable_media_status.has_eject))
+ sidebar->show_eject(removable_media_status.has_eject))
sidebar->Layout();
}
else
@@ -4168,7 +4277,7 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
sidebar->show_export(!ready_to_slice) |
sidebar->show_send(send_gcode_shown && !ready_to_slice) |
sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) |
- sidebar->show_disconnect(!ready_to_slice && removable_media_status.has_eject))
+ sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject))
sidebar->Layout();
}
}
@@ -4731,6 +4840,9 @@ void Plater::export_gcode(bool prefer_removable)
if (p->model.objects.empty())
return;
+ if (p->process_completed_with_error)//here
+ return;
+
// If possible, remove accents from accented latin characters.
// This function is useful for generating file names to be processed by legacy firmwares.
fs::path default_output_file;
@@ -4990,7 +5102,6 @@ void Plater::export_toolpaths_to_obj() const
p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str());
}
-
void Plater::reslice()
{
// Stop arrange and (or) optimize rotation tasks.
@@ -5676,6 +5787,16 @@ Mouse3DController& Plater::get_mouse3d_controller()
return p->mouse3d_controller;
}
+const NotificationManager* Plater::get_notification_manager() const
+{
+ return p->notification_manager;
+}
+
+NotificationManager* Plater::get_notification_manager()
+{
+ return p->notification_manager;
+}
+
bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index a08b19fa3..24e93c80e 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -47,6 +47,7 @@ class ObjectLayers;
class ObjectList;
class GLCanvas3D;
class Mouse3DController;
+class NotificationManager;
struct Camera;
class Bed3D;
class GLToolbar;
@@ -130,8 +131,9 @@ public:
bool show_reslice(bool show) const;
bool show_export(bool show) const;
bool show_send(bool show) const;
- bool show_disconnect(bool show)const;
+ bool show_eject(bool show)const;
bool show_export_removable(bool show) const;
+ bool get_eject_shown() const;
bool is_multifilament();
void update_mode();
bool is_collapsed();
@@ -338,6 +340,9 @@ public:
Mouse3DController& get_mouse3d_controller();
void set_bed_shape() const;
+
+ const NotificationManager* get_notification_manager() const;
+ NotificationManager* get_notification_manager();
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
class SuppressSnapshots
diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp
index c32613c46..7d316e77c 100644
--- a/src/slic3r/Utils/PresetUpdater.cpp
+++ b/src/slic3r/Utils/PresetUpdater.cpp
@@ -27,6 +27,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/format.hpp"
+#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Config/Version.hpp"
#include "slic3r/Config/Snapshot.hpp"
@@ -154,6 +155,9 @@ struct PresetUpdater::priv
bool cancel;
std::thread thread;
+ bool has_waiting_updates { false };
+ Updates waiting_updates;
+
priv();
void set_download_prefs(AppConfig *app_config);
@@ -165,6 +169,7 @@ struct PresetUpdater::priv
void check_install_indices() const;
Updates get_config_updates(const Semver& old_slic3r_version) const;
void perform_updates(Updates &&updates, bool snapshot = true) const;
+ void set_waiting_updates(Updates u);
};
PresetUpdater::priv::priv()
@@ -326,7 +331,15 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors)
continue;
}
Slic3r::rename_file(idx_path_temp, idx_path);
- index = std::move(new_index);
+ //if we rename path we need to change it in Index object too or create the object again
+ //index = std::move(new_index);
+ try {
+ index.load(idx_path);
+ }
+ catch (const std::exception& /* err */) {
+ BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path, vendor.name);
+ continue;
+ }
if (cancel)
return;
}
@@ -632,6 +645,12 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
}
}
+void PresetUpdater::priv::set_waiting_updates(Updates u)
+{
+ waiting_updates = u;
+ has_waiting_updates = true;
+}
+
PresetUpdater::PresetUpdater() :
p(new priv())
{}
@@ -690,9 +709,9 @@ void PresetUpdater::slic3r_update_notify()
}
}
-PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3r_version) const
+PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, bool no_notification) const
{
- if (! p->enabled_config_update) { return R_NOOP; }
+ if (! p->enabled_config_update) { return R_NOOP; }
auto updates = p->get_config_updates(old_slic3r_version);
if (updates.incompats.size() > 0) {
@@ -779,30 +798,38 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3
}
// regular update
- BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", updates.updates.size());
+ if (no_notification) {
+ BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
- std::vector updates_msg;
- for (const auto &update : updates.updates) {
- std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
- updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
- }
+ std::vector updates_msg;
+ for (const auto& update : updates.updates) {
+ std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
+ updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
+ }
- GUI::MsgUpdateConfig dlg(updates_msg);
+ GUI::MsgUpdateConfig dlg(updates_msg);
- const auto res = dlg.ShowModal();
- if (res == wxID_OK) {
- BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
- p->perform_updates(std::move(updates));
+ const auto res = dlg.ShowModal();
+ if (res == wxID_OK) {
+ BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
+ p->perform_updates(std::move(updates));
- // Reload global configuration
- auto *app_config = GUI::wxGetApp().app_config;
- GUI::wxGetApp().preset_bundle->load_presets(*app_config);
- GUI::wxGetApp().load_current_presets();
- return R_UPDATE_INSTALLED;
+ // Reload global configuration
+ auto* app_config = GUI::wxGetApp().app_config;
+ GUI::wxGetApp().preset_bundle->load_presets(*app_config);
+ GUI::wxGetApp().load_current_presets();
+ return R_UPDATE_INSTALLED;
+ }
+ else {
+ BOOST_LOG_TRIVIAL(info) << "User refused the update";
+ return R_UPDATE_REJECT;
+ }
} else {
- BOOST_LOG_TRIVIAL(info) << "User refused the update";
- return R_UPDATE_REJECT;
+ p->set_waiting_updates(updates);
+ GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAviable, *(GUI::wxGetApp().plater()->get_current_canvas3D()));
}
+
+ // MsgUpdateConfig will show after the notificaation is clicked
} else {
BOOST_LOG_TRIVIAL(info) << "No configuration updates available.";
}
@@ -825,5 +852,37 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool
p->perform_updates(std::move(updates), snapshot);
}
+void PresetUpdater::on_update_notification_confirm()
+{
+ if (!p->has_waiting_updates)
+ return;
+ BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
+
+ std::vector updates_msg;
+ for (const auto& update : p->waiting_updates.updates) {
+ std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
+ updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
+ }
+
+ GUI::MsgUpdateConfig dlg(updates_msg);
+
+ const auto res = dlg.ShowModal();
+ if (res == wxID_OK) {
+ BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
+ p->perform_updates(std::move(p->waiting_updates));
+
+ // Reload global configuration
+ auto* app_config = GUI::wxGetApp().app_config;
+ GUI::wxGetApp().preset_bundle->load_presets(*app_config);
+ GUI::wxGetApp().load_current_presets();
+ p->has_waiting_updates = false;
+ //return R_UPDATE_INSTALLED;
+ }
+ else {
+ BOOST_LOG_TRIVIAL(info) << "User refused the update";
+ //return R_UPDATE_REJECT;
+ }
+
+}
}
diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp
index e18695828..0ca363c61 100644
--- a/src/slic3r/Utils/PresetUpdater.hpp
+++ b/src/slic3r/Utils/PresetUpdater.hpp
@@ -35,16 +35,20 @@ public:
R_INCOMPAT_CONFIGURED,
R_UPDATE_INSTALLED,
R_UPDATE_REJECT,
+ R_UPDATE_NOTIFICATION
};
// If updating is enabled, check if updates are available in cache, if so, ask about installation.
// A false return value implies Slic3r should exit due to incompatibility of configuration.
// Providing old slic3r version upgrade profiles on upgrade of an application even in case
// that the config index installed from the Internet is equal to the index contained in the installation package.
- UpdateResult config_update(const Semver &old_slic3r_version) const;
+ // no_notification = force modal textbox, otherwise some cases only shows notification
+ UpdateResult config_update(const Semver &old_slic3r_version, bool no_notification) const;
// "Update" a list of bundles from resources (behaves like an online update).
void install_bundles_rsrc(std::vector bundles, bool snapshot = true) const;
+
+ void on_update_notification_confirm();
private:
struct priv;
std::unique_ptr p;