diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index b41894ebb6..0663eddbb7 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -518,9 +518,12 @@ int CLI::run(int argc, char **argv) outfile_final = sla_print.print_statistics().finalize_output_path(outfile); sla_archive.export_print(outfile_final, sla_print); } - if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) { - boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; - return 1; + if (outfile != outfile_final) { + if (Slic3r::rename_file(outfile, outfile_final)) { + boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; + return 1; + } + outfile = outfile_final; } boost::nowide::cout << "Slicing result exported to " << outfile << std::endl; } catch (const std::exception &ex) { @@ -588,9 +591,9 @@ int CLI::run(int argc, char **argv) // gui->autosave = m_config.opt_string("autosave"); GUI::GUI_App::SetInstance(gui); #if ENABLE_GCODE_VIEWER - gui->m_after_init_loads.set_params(load_configs, m_extra_config, m_input_files, start_as_gcodeviewer); + gui->after_init_loads.set_params(load_configs, m_extra_config, m_input_files, start_as_gcodeviewer); #else - gui->m_after_init_loads.set_params(load_configs, m_extra_config, m_input_files); + gui->after_init_loads.set_params(load_configs, m_extra_config, m_input_files); #endif // ENABLE_GCODE_VIEWER /* #if ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 01e46d2271..3061693a75 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -577,6 +577,16 @@ void Print::config_diffs( } } +std::vector Print::print_object_ids() const +{ + std::vector out; + // Reserve one more for the caller to append the ID of the Print itself. + out.reserve(m_objects.size() + 1); + for (const PrintObject *print_object : m_objects) + out.emplace_back(print_object->id()); + return out; +} + Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config) { #ifdef _DEBUG diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index a389ef00da..29cdc7249d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -370,6 +370,8 @@ public: // a cancellation callback is executed to stop the background processing before the operation. void clear() override; bool empty() const override { return m_objects.empty(); } + // List of existing PrintObject IDs, to remove notifications for non-existent IDs. + std::vector print_object_ids() const override; ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 647c24c1ce..bfbabd06b5 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -348,6 +348,8 @@ public: // The Print is empty either after clear() or after apply() over an empty model, // or after apply() over a model, where no object is printable (all outside the print volume). virtual bool empty() const = 0; + // List of existing PrintObject IDs, to remove notifications for non-existent IDs. + virtual std::vector print_object_ids() const = 0; // Validate the print, return empty string if valid, return error if process() cannot (or should not) be started. virtual std::string validate() const { return std::string(); } @@ -406,7 +408,7 @@ public: // set to an ObjectID of a Print or a PrintObject based on flags // (whether UPDATE_PRINT_STEP_WARNINGS or UPDATE_PRINT_OBJECT_STEP_WARNINGS is set). ObjectID warning_object_id; - // For which Print or PrintObject step a new warning is beeing issued? + // For which Print or PrintObject step a new warning is being issued? int warning_step { -1 }; }; typedef std::function status_callback_type; diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 7e884b6e3c..77cee77a9d 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -357,13 +357,26 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m double r = random_triangle(rng); size_t idx_triangle = std::min(std::upper_bound(areas.begin(), areas.end(), (float)r) - areas.begin(), areas.size() - 1) * 3; // Select a random point on the triangle. - double u = float(std::sqrt(random_float(rng))); - double v = float(random_float(rng)); const Vec2f &a = triangles[idx_triangle ++]; const Vec2f &b = triangles[idx_triangle++]; const Vec2f &c = triangles[idx_triangle]; - const Vec2f x = a * (1.f - u) + b * (u * (1.f - v)) + c * (v * u); - out.emplace_back(x); +#if 0 + // https://www.cs.princeton.edu/~funk/tog02.pdf + // page 814, formula 1. + double u = float(std::sqrt(random_float(rng))); + double v = float(random_float(rng)); + out.emplace_back(a * (1.f - u) + b * (u * (1.f - v)) + c * (v * u)); +#else + // Greg Turk, Graphics Gems + // https://devsplorer.wordpress.com/2019/08/07/find-a-random-point-on-a-plane-using-barycentric-coordinates-in-unity/ + double u = float(random_float(rng)); + double v = float(random_float(rng)); + if (u + v >= 1.f) { + u = 1.f - u; + v = 1.f - v; + } + out.emplace_back(a + u * (b - a) + v * (c - a)); +#endif } } return out; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index a2d2ff6209..54cf55caba 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -175,6 +175,16 @@ static std::vector sla_instances(const ModelObject &mo return instances; } +std::vector SLAPrint::print_object_ids() const +{ + std::vector out; + // Reserve one more for the caller to append the ID of the Print itself. + out.reserve(m_objects.size() + 1); + for (const SLAPrintObject *print_object : m_objects) + out.emplace_back(print_object->id()); + return out; +} + SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config) { #ifdef _DEBUG diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 5ca1b2b8e7..a4305e0c31 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -420,6 +420,8 @@ public: void clear() override; bool empty() const override { return m_objects.empty(); } + // List of existing PrintObject IDs, to remove notifications for non-existent IDs. + std::vector print_object_ids() const; ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; void set_task(const TaskParams ¶ms) override; void process() override; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e87fa383d9..c2717761fc 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -640,16 +640,17 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool error = true; break; } - if(state) { + auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager(); + if (state) { if(error) - wxGetApp().plater()->get_notification_manager()->push_plater_error_notification(text,*(wxGetApp().plater()->get_current_canvas3D())); + notification_manager.push_plater_error_notification(text,*(wxGetApp().plater()->get_current_canvas3D())); else - wxGetApp().plater()->get_notification_manager()->push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); + notification_manager.push_plater_warning_notification(text, *(wxGetApp().plater()->get_current_canvas3D())); } else { if (error) - wxGetApp().plater()->get_notification_manager()->close_plater_error_notification(text); + notification_manager.close_plater_error_notification(text); else - wxGetApp().plater()->get_notification_manager()->close_plater_warning_notification(text); + notification_manager.close_plater_warning_notification(text); } /* @@ -2087,7 +2088,7 @@ void GLCanvas3D::render() wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); - wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overelay_window_width(), get_slope_window_width()); + wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); wxGetApp().imgui()->render(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 886944488f..a76d9f4a01 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -243,7 +243,7 @@ private: static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); static Rect get_bar_rect_screen(const GLCanvas3D& canvas); static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); - static float get_overelay_window_width() { return LayersEditing::s_overelay_window_width; } + static float get_overlay_window_width() { return LayersEditing::s_overelay_window_width; } float object_max_z() const { return m_object_max_z; } @@ -885,8 +885,7 @@ private: bool _activate_search_toolbar_item(); bool _deactivate_collapse_toolbar_items(); - float get_overelay_window_width() { return LayersEditing::get_overelay_window_width(); } - float get_slope_window_width() { return Slope::get_window_width(); } + float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } static std::vector _parse_colors(const std::vector& colors); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bbeb1aada1..2120d35a64 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -533,7 +533,7 @@ static void generic_exception_handle() } } -void GUI_App::AFTER_INIT_LOADS::on_loads(GUI_App* gui) +void GUI_App::AfterInitLoads::on_loads(GUI_App* gui) { if (!gui->initialized()) return; @@ -773,7 +773,7 @@ bool GUI_App::on_init_inner() 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())); + this->plater_->get_notification_manager()->push_notification(NotificationType::NewAppAvailable, *(this->plater_->get_current_canvas3D())); } } }); @@ -847,7 +847,10 @@ bool GUI_App::on_init_inner() static bool update_gui_after_init = true; if (update_gui_after_init) { update_gui_after_init = false; - m_after_init_loads.on_loads(this); +#ifdef WIN32 + this->mainframe->register_win32_callbacks(); +#endif + this->after_init_loads.on_loads(this); } // Preset updating & Configwizard are done after the above initializations, diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index dd2d54c0d1..fa2872a538 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -143,7 +143,7 @@ private: size_t m_instance_hash_int; // parameters needed for the after OnInit() loads - struct AFTER_INIT_LOADS + struct AfterInitLoads { std::vector m_load_configs; DynamicPrintConfig m_extra_config; @@ -272,7 +272,7 @@ public: PresetUpdater* preset_updater{ nullptr }; MainFrame* mainframe{ nullptr }; Plater* plater_{ nullptr }; - AFTER_INIT_LOADS m_after_init_loads; + AfterInitLoads after_init_loads; PresetUpdater* get_preset_updater() { return preset_updater; } diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp index a8b1b86577..263a1683c3 100644 --- a/src/slic3r/GUI/InstanceCheck.cpp +++ b/src/slic3r/GUI/InstanceCheck.cpp @@ -35,9 +35,9 @@ namespace instance_check_internal }; static CommandLineAnalysis process_command_line(int argc, char** argv) { - CommandLineAnalysis ret { false }; - if (argc < 2) - return ret; + CommandLineAnalysis ret; + //if (argc < 2) + // return ret; std::vector arguments { argv[0] }; for (size_t i = 1; i < argc; ++i) { const std::string token = argv[i]; @@ -254,7 +254,8 @@ bool instance_check(int argc, char** argv, bool app_config_single_instance) GUI::wxGetApp().init_single_instance_checker(lock_name + ".lock", data_dir() + "/cache/"); if (cla.should_send.value() && GUI::wxGetApp().single_instance_checker()->IsAnotherRunning()) { #else // mac & linx - if (*cla.should_send && instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/")) { + // get_lock() creates the lockfile therefore *cla.should_send is checked after + if (instance_check_internal::get_lock(lock_name + ".lock", data_dir() + "/cache/") && *cla.should_send) { #endif instance_check_internal::send_message(cla.cl_string, lock_name); BOOST_LOG_TRIVIAL(info) << "instance check: Another instance found. This instance will terminate."; diff --git a/src/slic3r/GUI/InstanceCheckMac.mm b/src/slic3r/GUI/InstanceCheckMac.mm index 7b3f1b222c..9969b1a7b2 100644 --- a/src/slic3r/GUI/InstanceCheckMac.mm +++ b/src/slic3r/GUI/InstanceCheckMac.mm @@ -19,6 +19,7 @@ -(void)message_update:(NSNotification *)msg { + [self bring_forward]; //pass message Slic3r::GUI::wxGetApp().other_instance_message_handler()->handle_message(std::string([msg.userInfo[@"data"] UTF8String])); } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8db0bba5b9..67ccd385e8 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -217,65 +217,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S update_title(); // declare events - Bind(wxEVT_CREATE, [this](wxWindowCreateEvent& event) { - -#ifdef _WIN32 - //static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }; - //static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }; - //static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f }; - static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; - - // Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration. - DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 }; - NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); - NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID; - m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); - -// or register for file handle change? -// DEV_BROADCAST_HANDLE NotificationFilter = { 0 }; -// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE); -// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE; - - // Using Win32 Shell API to register for media insert / removal events. - LPITEMIDLIST ppidl; - if (SHGetSpecialFolderLocation(this->GetHWND(), CSIDL_DESKTOP, &ppidl) == NOERROR) { - SHChangeNotifyEntry shCNE; - shCNE.pidl = ppidl; - shCNE.fRecursive = TRUE; - // Returns a positive integer registration identifier (ID). - // Returns zero if out of memory or in response to invalid parameters. - m_ulSHChangeNotifyRegister = SHChangeNotifyRegister(this->GetHWND(), // Hwnd to receive notification - SHCNE_DISKEVENTS, // Event types of interest (sources) - SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED, - //SHCNE_UPDATEITEM, // Events of interest - use SHCNE_ALLEVENTS for all events - WM_USER_MEDIACHANGED, // Notification message to be sent upon the event - 1, // Number of entries in the pfsne array - &shCNE); // Array of SHChangeNotifyEntry structures that - // contain the notifications. This array should - // always be set to one when calling SHChnageNotifyRegister - // or SHChangeNotifyDeregister will not work properly. - assert(m_ulSHChangeNotifyRegister != 0); // Shell notification failed - } else { - // Failed to get desktop location - assert(false); - } - - { - static constexpr int device_count = 1; - RAWINPUTDEVICE devices[device_count] = { 0 }; - // multi-axis mouse (SpaceNavigator, etc.) - devices[0].usUsagePage = 0x01; - devices[0].usUsage = 0x08; - if (! RegisterRawInputDevices(devices, device_count, sizeof(RAWINPUTDEVICE))) - BOOST_LOG_TRIVIAL(error) << "RegisterRawInputDevices failed"; - } -#endif // _WIN32 - - // propagate event - event.Skip(); - }); - Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) { event.Veto(); @@ -286,6 +227,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S event.Skip(); }); + //FIXME it seems this method is not called on application start-up, at least not on Windows. Why? + // The same applies to wxEVT_CREATE, it is not being called on startup on Windows. Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& event) { if (m_plater != nullptr && event.GetActive()) m_plater->on_activate(); @@ -656,6 +599,62 @@ void MainFrame::init_tabpanel() } } +#ifdef WIN32 +void MainFrame::register_win32_callbacks() +{ + //static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }; + //static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }; + //static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f }; + static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; + + // Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration. + DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 }; + NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID; + m_hDeviceNotify = ::RegisterDeviceNotification(this->GetHWND(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE); + +// or register for file handle change? +// DEV_BROADCAST_HANDLE NotificationFilter = { 0 }; +// NotificationFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE); +// NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE; + + // Using Win32 Shell API to register for media insert / removal events. + LPITEMIDLIST ppidl; + if (SHGetSpecialFolderLocation(this->GetHWND(), CSIDL_DESKTOP, &ppidl) == NOERROR) { + SHChangeNotifyEntry shCNE; + shCNE.pidl = ppidl; + shCNE.fRecursive = TRUE; + // Returns a positive integer registration identifier (ID). + // Returns zero if out of memory or in response to invalid parameters. + m_ulSHChangeNotifyRegister = SHChangeNotifyRegister(this->GetHWND(), // Hwnd to receive notification + SHCNE_DISKEVENTS, // Event types of interest (sources) + SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED, + //SHCNE_UPDATEITEM, // Events of interest - use SHCNE_ALLEVENTS for all events + WM_USER_MEDIACHANGED, // Notification message to be sent upon the event + 1, // Number of entries in the pfsne array + &shCNE); // Array of SHChangeNotifyEntry structures that + // contain the notifications. This array should + // always be set to one when calling SHChnageNotifyRegister + // or SHChangeNotifyDeregister will not work properly. + assert(m_ulSHChangeNotifyRegister != 0); // Shell notification failed + } else { + // Failed to get desktop location + assert(false); + } + + { + static constexpr int device_count = 1; + RAWINPUTDEVICE devices[device_count] = { 0 }; + // multi-axis mouse (SpaceNavigator, etc.) + devices[0].usUsagePage = 0x01; + devices[0].usUsage = 0x08; + if (! RegisterRawInputDevices(devices, device_count, sizeof(RAWINPUTDEVICE))) + BOOST_LOG_TRIVIAL(error) << "RegisterRawInputDevices failed"; + } +} +#endif // _WIN32 + void MainFrame::create_preset_tabs() { wxGetApp().update_label_colours_from_appconfig(); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 9be9bbad91..3a8443cb80 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -156,6 +156,9 @@ public: void create_preset_tabs(); void add_created_tab(Tab* panel); bool is_active_and_shown_tab(Tab* tab); + // Register Win32 RawInput callbacks (3DConnexion) and removable media insert / remove callbacks. + // Called from wxEVT_ACTIVATE, as wxEVT_CREATE was not reliable (bug in wxWidgets?). + void register_win32_callbacks(); #if ENABLE_GCODE_VIEWER void init_menubar_as_editor(); void init_menubar_as_gcodeviewer(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e05931d873..cb499b3c6e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -9,26 +9,22 @@ #include #include -#include #include +#include - - -#define NOTIFICATION_MAX_MOVE 3.0f - -#define GAP_WIDTH 10.0f -#define SPACE_RIGHT_PANEL 10.0f +static constexpr float GAP_WIDTH = 10.0f; +static constexpr float 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); +wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent); namespace Notifications_Internal{ - void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity) + static inline 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)); @@ -36,11 +32,34 @@ namespace Notifications_Internal{ ImGui::PushStyleColor(idx, col); } } -//ScalableBitmap bmp_icon; + +#if 1 +// Reuse ImGUI Windows. +int NotificationManager::NotificationIDProvider::allocate_id() +{ + int id; + if (m_released_ids.empty()) + id = ++m_next_id; + else { + id = m_released_ids.back(); + m_released_ids.pop_back(); + } + return id; +} +void NotificationManager::NotificationIDProvider::release_id(int id) +{ + m_released_ids.push_back(id); +} +#else +// Don't reuse ImGUI Windows, allocate a new ID every time. +int NotificationManager::NotificationIDProvider::allocate_id() { return ++ m_next_id; } +void NotificationManager::NotificationIDProvider::release_id(int) {} +#endif + //------PopNotification-------- -NotificationManager::PopNotification::PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler) : +NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) : m_data (n) - , m_id (id) + , m_id_provider (id_provider) , m_remaining_time (n.duration) , m_last_remaining_time (n.duration) , m_counting_down (n.duration != 0) @@ -51,7 +70,7 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n, { //init(); } -NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width, bool move_from_slope, float slope_width) +NotificationManager::PopNotification::RenderResult NotificationManager::PopNotification::render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width) { if (!m_initialized) { init(); @@ -71,9 +90,8 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif Size cnv_size = canvas.get_canvas_size(); ImGuiWrapper& imgui = *wxGetApp().imgui(); bool shown = true; - std::string name; ImVec2 mouse_pos = ImGui::GetMousePos(); - float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : (move_from_slope ? slope_width /*+ m_line_height * 0.3f*/ : 0)); + float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); if (m_line_height != ImGui::CalcTextSize("A").y) init(); @@ -134,8 +152,15 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif } //name of window - probably indentifies window and is shown so last_end add whitespaces according to id - for (size_t i = 0; i < m_id; i++) - name += " "; + if (! m_id) + m_id = m_id_provider.allocate_id(); + std::string name; + { + // Create a unique ImGUI window name. The name may be recycled using a name of an already released notification. + char buf[32]; + sprintf(buf, "!!Ntfctn%d", m_id); + name = buf; + } if (imgui.begin(name, &shown, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar )) { if (shown) { @@ -234,12 +259,9 @@ void NotificationManager::PopNotification::init() } 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 = m_multiline ? + m_lines_count * m_line_height : + 2 * m_line_height; m_window_height += 1 * m_line_height; // top and bottom } @@ -531,13 +553,13 @@ void NotificationManager::PopNotification::on_text_click() if (m_evt_handler != nullptr) wxPostEvent(m_evt_handler, ExportGcodeNotificationClickedEvent(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED)); break; - case NotificationType::PresetUpdateAviable : + case NotificationType::PresetUpdateAvailable : //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)); + wxPostEvent(m_evt_handler, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); break; - case NotificationType::NewAppAviable: + case NotificationType::NewAppAvailable: wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); break; default: @@ -562,8 +584,8 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } -NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool large) : - NotificationManager::PopNotification(n, id, evt_handler) +NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : + NotificationManager::PopNotification(n, id_provider, evt_handler) { set_large(large); } @@ -593,14 +615,13 @@ void NotificationManager::SlicingCompleteLargeNotification::render_text(ImGuiWra 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) +void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const std::string &info) { m_print_info = info; m_has_print_info = true; - if(m_is_large) + if (m_is_large) m_lines_count = 2; } void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l) @@ -618,7 +639,8 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : 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); + boost::bind(&NotificationData::type, _1) == type); + assert(it != basic_notifications.end()); if (it != basic_notifications.end()) push_notification_data( *it, canvas, timestamp); } @@ -628,21 +650,16 @@ void NotificationManager::push_notification(const std::string& text, GLCanvas3D& } 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; + int duration = 0; + switch (level) { + case NotificationLevel::RegularNotification: duration = 10; break; + case NotificationLevel::ErrorNotification: break; + case NotificationLevel::ImportantNotification: break; default: - break; + assert(false); + return; } + push_notification_data({ NotificationType::CustomNotification, level, duration, text }, canvas, timestamp); } void NotificationManager::push_slicing_error_notification(const std::string& text, GLCanvas3D& canvas) { @@ -650,15 +667,15 @@ void NotificationManager::push_slicing_error_notification(const std::string& tex 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) +void NotificationManager::push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, ObjectID oid, int warning_step) { NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }; - auto notification = std::make_unique(data, m_next_id++, m_evt_handler); - notification->set_object_id(oid); - notification->set_warning_step(warning_step); + auto notification = std::make_unique(data, m_id_provider, m_evt_handler); + notification->object_id = oid; + notification->warning_step = warning_step; if (push_notification_data(std::move(notification), canvas, 0)) { - notification->set_gray(gray); + m_pop_notifications.back()->set_gray(gray); } } void NotificationManager::push_plater_error_notification(const std::string& text, GLCanvas3D& canvas) @@ -703,6 +720,7 @@ void NotificationManager::set_all_slicing_warnings_gray(bool g) } } } +/* void NotificationManager::set_slicing_warning_gray(const std::string& text, bool g) { for (std::unique_ptr ¬ification : m_pop_notifications) { @@ -711,6 +729,7 @@ void NotificationManager::set_slicing_warning_gray(const std::string& text, bool } } } +*/ void NotificationManager::close_slicing_errors_and_warnings() { for (std::unique_ptr ¬ification : m_pop_notifications) { @@ -723,17 +742,17 @@ void NotificationManager::push_slicing_complete_notification(GLCanvas3D& canvas, { std::string hypertext; int time = 10; - if (has_error_notification()) + if (has_slicing_error_notification()) return; if (large) { hypertext = _u8L("Export G-Code."); time = 0; } NotificationData data{ NotificationType::SlicingComplete, NotificationLevel::RegularNotification, time, _u8L("Slicing finished."), hypertext }; - push_notification_data(std::make_unique(data, m_next_id++, m_evt_handler, large), + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, large), canvas, timestamp); } -void NotificationManager::set_slicing_complete_print_time(std::string info) +void NotificationManager::set_slicing_complete_print_time(const std::string &info) { for (std::unique_ptr ¬ification : m_pop_notifications) { if (notification->get_type() == NotificationType::SlicingComplete) { @@ -759,26 +778,18 @@ void NotificationManager::close_notification_of_type(const NotificationType type } } } -void NotificationManager::compare_warning_oids(const std::vector& living_oids) +void NotificationManager::remove_slicing_warnings_of_released_objects(const std::vector& living_oids) { - for (std::unique_ptr ¬ification : m_pop_notifications) { + for (std::unique_ptr ¬ification : m_pop_notifications) if (notification->get_type() == NotificationType::SlicingWarning) { - auto w = dynamic_cast(notification.get()); - bool found = false; - for (size_t oid : living_oids) { - if (w->get_object_id() == oid) { - found = true; - break; - } - } - if (!found) + if (! std::binary_search(living_oids.begin(), living_oids.end(), + static_cast(notification.get())->object_id)) notification->close(); } - } } bool NotificationManager::push_notification_data(const NotificationData ¬ification_data, GLCanvas3D& canvas, int timestamp) { - return push_notification_data(std::make_unique(notification_data, m_next_id++, m_evt_handler), canvas, timestamp); + return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), canvas, timestamp); } bool NotificationManager::push_notification_data(std::unique_ptr notification, GLCanvas3D& canvas, int timestamp) { @@ -790,23 +801,23 @@ bool NotificationManager::push_notification_data(std::unique_ptrfind_older(notification.get())) { - m_pop_notifications.emplace_back(std::move(notification)); - canvas.request_extra_frame(); - return true; - } else { + if (this->activate_existing(notification.get())) { m_pop_notifications.back()->update(notification->get_data()); canvas.request_extra_frame(); return false; + } else { + m_pop_notifications.emplace_back(std::move(notification)); + canvas.request_extra_frame(); + return true; } } -void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width, float slope_width) +void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width) { float last_x = 0.0f; float current_height = 0.0f; bool request_next_frame = false; bool render_main = false; - bool hovered = 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();) { @@ -814,7 +825,7 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay it = m_pop_notifications.erase(it); } else { (*it)->set_paused(m_hovered); - PopNotification::RenderResult res = (*it)->render(canvas, last_x, m_move_from_overlay, overlay_width, m_move_from_slope, slope_width); + PopNotification::RenderResult res = (*it)->render(canvas, last_x, m_move_from_overlay, overlay_width); if (res != PopNotification::RenderResult::Finished) { last_x = (*it)->get_top() + GAP_WIDTH; current_height = std::max(current_height, (*it)->get_current_top()); @@ -837,26 +848,34 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay if (!top_level_wnd->IsActive()) return; - if (!m_hovered && m_last_time < wxGetLocalTime()) { - if (wxGetLocalTime() - m_last_time == 1) + // Control the fade-out. + // time in seconds + long now = wxGetLocalTime(); + // Pausing fade-out when the mouse is over some notification. + if (!m_hovered && m_last_time < now) { - for(auto ¬ification : m_pop_notifications) + if (now - m_last_time == 1) { - notification->substract_remaining_time(); + for (auto ¬ification : m_pop_notifications) + { + notification->substract_remaining_time(); + } } + m_last_time = now; } - m_last_time = wxGetLocalTime(); } if (request_next_frame) + //FIXME this is very expensive for fade-out control. + // If any of the notifications is fading out, 100% of the CPU/GPU is consumed. canvas.request_extra_frame(); } - void NotificationManager::sort_notifications() { - std::sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](const std::unique_ptr &n1, const std::unique_ptr &n2) { + // Stable sorting, so that the order of equal ranges is stable. + std::stable_sort(m_pop_notifications.begin(), m_pop_notifications.end(), [](const std::unique_ptr &n1, const std::unique_ptr &n2) { int n1l = (int)n1->get_data().level; int n2l = (int)n2->get_data().level; if (n1l == n2l && n1->get_is_gray() && !n2->get_is_gray()) @@ -865,20 +884,20 @@ void NotificationManager::sort_notifications() }); } -bool NotificationManager::find_older(NotificationManager::PopNotification* notification) +bool NotificationManager::activate_existing(const NotificationManager::PopNotification* notification) { - NotificationType type = notification->get_type(); - std::string text = notification->get_data().text1; + NotificationType new_type = notification->get_type(); + const std::string &new_text = notification->get_data().text1; for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { - if((*it)->get_type() == type && !(*it)->get_finished()) { - if (type == NotificationType::CustomNotification || type == NotificationType::PlaterWarning) { - if (!(*it)->compare_text(text)) + if ((*it)->get_type() == new_type && !(*it)->get_finished()) { + if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) { + if (!(*it)->compare_text(new_text)) continue; - }else if (type == NotificationType::SlicingWarning) { - auto w1 = dynamic_cast(notification); - auto w2 = dynamic_cast(it->get()); + } else if (new_type == NotificationType::SlicingWarning) { + auto w1 = dynamic_cast(notification); + auto w2 = dynamic_cast(it->get()); if (w1 != nullptr && w2 != nullptr) { - if (!(*it)->compare_text(text) || w1->get_object_id() != w2->get_object_id()) { + if (!(*it)->compare_text(new_text) || w1->object_id != w2->object_id) { continue; } } else { @@ -894,7 +913,7 @@ bool NotificationManager::find_older(NotificationManager::PopNotification* notif return false; } -void NotificationManager::set_in_preview(bool preview) +void NotificationManager::set_in_preview(bool preview) { m_in_preview = preview; for (std::unique_ptr ¬ification : m_pop_notifications) { @@ -902,18 +921,12 @@ void NotificationManager::set_in_preview(bool preview) notification->hide(preview); } } -bool NotificationManager::has_error_notification() -{ - for (std::unique_ptr ¬ification : m_pop_notifications) { - if (notification->get_data().level == NotificationLevel::ErrorNotification) - return true; - } - return false; -} -void NotificationManager::dpi_changed() +bool NotificationManager::has_slicing_error_notification() { - + return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) { + return n->get_type() == NotificationType::SlicingError; + }); } }//namespace GUI diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 1972c92440..49de00a9f4 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -4,6 +4,8 @@ #include "Event.hpp" #include "I18N.hpp" +#include + #include #include #include @@ -16,8 +18,8 @@ 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); +using PresetUpdateAvailableClickedEvent = SimpleEvent; +wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent); class GLCanvas3D; class ImGuiWrapper; @@ -25,22 +27,36 @@ class ImGuiWrapper; enum class NotificationType { CustomNotification, + // Notification on end of slicing and G-code processing (the full G-code preview is available). + // Contains a hyperlink to export the G-code to a removable media. SlicingComplete, - SlicingNotPossible, +// SlicingNotPossible, + // Notification on end of export to a removable media, with hyperling to eject the external media. ExportToRemovableFinished, + // Works on OSX only. + //FIXME Do we want to have it on Linux and Windows? Is it possible to get the Disconnect event on Windows? Mouse3dDisconnected, - Mouse3dConnected, - NewPresetsAviable, - NewAppAviable, - PresetUpdateAviable, - LoadingFailed, - ValidateError, // currently not used - instead Slicing error is used for both slicing and validate errors +// Mouse3dConnected, +// NewPresetsAviable, + // Notification on the start of PrusaSlicer, when a new PrusaSlicer version is published. + // Contains a hyperlink to open a web browser pointing to the PrusaSlicer download location. + NewAppAvailable, + // Notification on the start of PrusaSlicer, when updates of system profiles are detected. + // Contains a hyperlink to execute installation of the new system profiles. + PresetUpdateAvailable, +// LoadingFailed, + // Not used - instead Slicing error is used for both slicing and validate errors. +// ValidateError, + // Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background + // thread thowing a SlicingError exception. SlicingError, + // Slicing warnings, issued by the slicing process. + // Slicing warnings are registered for a particular Print milestone or a PrintObject and its milestone. SlicingWarning, + // Object partially outside the print volume. Cannot slice. PlaterError, - PlaterWarning, - ApplyError - + // Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled. + PlaterWarning }; class NotificationManager @@ -48,46 +64,62 @@ class NotificationManager public: enum class NotificationLevel : int { - ErrorNotification = 4, - WarningNotification = 3, - ImportantNotification = 2, - RegularNotification = 1, + // The notifications will be presented in the order of importance, thus these enum values + // are sorted by the importance. + // "Good to know" notification, usually but not always with a quick fade-out. + RegularNotification = 1, + // Information notification without a fade-out or with a longer fade-out. + ImportantNotification, + // Warning, no fade-out. + WarningNotification, + // Error, no fade-out. + ErrorNotification, }; NotificationManager(wxEvtHandler* evt_handler); - // only type means one of basic_notification (see below) + // Push a prefabricated notification from basic_notifications (see the table at the end of this file). void push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp = 0); - // only text means Undefined type + // Push a NotificationType::CustomNotification with NotificationLevel::RegularNotification and 10s fade out interval. void push_notification(const std::string& text, GLCanvas3D& canvas, int timestamp = 0); + // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotification. + // ErrorNotification and ImportantNotification are never faded out. void push_notification(const std::string& text, NotificationLevel level, GLCanvas3D& canvas, int timestamp = 0); - // creates Slicing Error notification with custom text + // Creates Slicing Error notification with a custom text and no fade out. 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); + // Creates Slicing Warning notification with a custom text and no fade out. + void push_slicing_warning_notification(const std::string& text, bool gray, GLCanvas3D& canvas, ObjectID 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 set_slicing_warning_gray(const std::string& text, bool g); + // immediately stops showing slicing errors void close_slicing_errors_and_warnings(); - void compare_warning_oids(const std::vector& living_oids); + // Release those slicing warnings, which refer to an ObjectID, which is not in the list. + // living_oids is expected to be sorted. + void remove_slicing_warnings_of_released_objects(const std::vector& living_oids); + // Object partially outside of the printer working space, cannot print. No fade out. void push_plater_error_notification(const std::string& text, GLCanvas3D& canvas); + // Object fully out of the printer working space and such. No fade out. void push_plater_warning_notification(const std::string& text, GLCanvas3D& canvas); - // Closes error or warning of same text + // Closes error or warning of the same text void close_plater_error_notification(const std::string& text); void close_plater_warning_notification(const std::string& text); - // creates special notification slicing complete - // if large = true prints printing time and export button + // Creates special notification slicing complete. + // If large = true (Plater side bar is closed), then printing time and export button is shown + // at the notification and fade-out is disabled. Otherwise the fade out time is set to 10s. void push_slicing_complete_notification(GLCanvas3D& canvas, int timestamp, bool large); - void set_slicing_complete_print_time(std::string info); + // Add a print time estimate to an existing SlicingComplete notification. + void set_slicing_complete_print_time(const std::string &info); + // Called when the side bar changes its visibility, as the "slicing complete" notification supplements + // the "slicing info" normally shown at the side bar. void set_slicing_complete_large(bool large); // renders notifications in queue and deletes expired ones - void render_notifications(GLCanvas3D& canvas, float overlay_width, float slope_width); + void render_notifications(GLCanvas3D& canvas, float overlay_width); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); - void dpi_changed(); + // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. void set_in_preview(bool preview); // Move to left to avoid colision with variable layer height gizmo void set_move_from_overlay(bool move) { m_move_from_overlay = move; } @@ -97,10 +129,25 @@ private: struct NotificationData { NotificationType type; NotificationLevel level; + // Fade out time const int duration; const std::string text1; - const std::string hypertext = std::string(); - const std::string text2 = std::string(); + const std::string hypertext; + const std::string text2; + }; + + // Cache of IDs to identify and reuse ImGUI windows. + class NotificationIDProvider + { + public: + int allocate_id(); + void release_id(int id); + + private: + // Next ID used for naming the ImGUI windows. + int m_next_id{ 1 }; + // IDs of ImGUI windows, which were released and they are ready for reuse. + std::vector m_released_ids; }; //Pop notification - shows only once to user. @@ -115,9 +162,9 @@ private: Countdown, Hovered }; - PopNotification(const NotificationData &n, const int id, wxEvtHandler* evt_handler); - virtual ~PopNotification() = default; - RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width, bool move_from_slope, float slope_width); + PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); + virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } + RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width); // close will dissapear notification on next render void close() { m_close_pending = true; } // data from newer notification of same type @@ -136,6 +183,7 @@ private: void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); void hide(bool h) { m_hidden = h; } + protected: // Call after every size change void init(); @@ -161,7 +209,9 @@ private: const NotificationData m_data; - int m_id; + // For reusing ImGUI windows. + NotificationIDProvider &m_id_provider; + int m_id { 0 }; bool m_initialized { false }; // Main text std::string m_text1; @@ -203,17 +253,18 @@ private: //if multiline = true, notification is showing all lines(>2) bool m_multiline { false }; int m_lines_count{ 1 }; + // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. wxEvtHandler* m_evt_handler; }; class SlicingCompleteLargeNotification : public PopNotification { public: - SlicingCompleteLargeNotification(const NotificationData& n, const int id, wxEvtHandler* evt_handler, bool largeds); + SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds); void set_large(bool l); bool get_large() { return m_is_large; } - void set_print_info(std::string info); + void set_print_info(const std::string &info); protected: virtual void render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, @@ -228,14 +279,9 @@ private: 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; + SlicingWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} + ObjectID object_id; + int warning_step; }; //pushes notification into the queue of notifications that are rendered @@ -243,31 +289,38 @@ private: bool push_notification_data(const NotificationData& notification_data, GLCanvas3D& canvas, int timestamp); bool push_notification_data(std::unique_ptr 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); + bool activate_existing(const NotificationManager::PopNotification* notification); + // Put the more important notifications to the bottom of the list. void sort_notifications(); - bool has_error_notification(); + // If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed. + bool has_slicing_error_notification(); + // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. wxEvtHandler* m_evt_handler; + // Cache of IDs to identify and reuse ImGUI windows. + NotificationIDProvider m_id_provider; std::deque> m_pop_notifications; - int m_next_id { 1 }; + // Last render time in seconds for fade out control. long m_last_time { 0 }; + // When mouse hovers over some notification, the fade-out of all notifications is suppressed. bool m_hovered { false }; - //timestamps used for slining finished - notification could be gone so it needs to be stored here + //timestamps used for slicing finished - notification could be gone so it needs to be stored here std::unordered_set m_used_timestamps; + // True if G-code preview is active. False if the Plater is active. bool m_in_preview { false }; + // True if the layer editing is enabled in Plater, so that the notifications are shifted left of it. bool m_move_from_overlay { false }; - bool m_move_from_slope{ false }; //prepared (basic) notifications const std::vector basic_notifications = { - {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, +// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, - {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, - {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, - {NotificationType::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::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::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 585786bb18..7f89dc9e96 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1714,7 +1714,8 @@ struct Plater::priv void clear_warnings(); void add_warning(const Slic3r::PrintStateBase::Warning &warning, size_t oid); - void actualizate_warnings(const Model& model, size_t print_oid); + // Update notification manager with the current state of warnings produced by the background process (slicing). + void actualize_slicing_warnings(const PrintBase &print); // 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 @@ -2023,7 +2024,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // ENABLE_GCODE_VIEWER 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_PRESET_UPDATE_AVAILABLE_CLICKED, [this](PresetUpdateAvailableClickedEvent&) { 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); @@ -2885,8 +2886,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool //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); + actualize_slicing_warnings(*this->background_process.current_print()); show_warning_dialog = false; process_completed_with_error = false; } @@ -3474,7 +3474,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) // 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); + notification_manager->push_slicing_warning_notification(warning.message, false, *q->get_current_canvas3D(), object_id, warning_step); add_warning(warning, object_id.id); } } @@ -3483,7 +3483,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) 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) { @@ -3520,19 +3519,17 @@ void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, s } current_warnings.emplace_back(std::pair(warning, oid)); } -void Plater::priv::actualizate_warnings(const Model& model, size_t print_oid) +void Plater::priv::actualize_slicing_warnings(const PrintBase &print) { - if (model.objects.size() == 0) { + std::vector ids = print.print_object_ids(); + if (ids.empty()) { clear_warnings(); return; } - 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); + ids.emplace_back(print.id()); + std::sort(ids.begin(), ids.end()); + notification_manager->remove_slicing_warnings_of_released_objects(ids); + notification_manager->set_all_slicing_warnings_gray(true); } void Plater::priv::clear_warnings() { diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 14b9fb0c4a..b424bafc4e 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -826,7 +826,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3 } } else { p->set_waiting_updates(updates); - GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAviable, *(GUI::wxGetApp().plater()->get_current_canvas3D())); + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable, *(GUI::wxGetApp().plater()->get_current_canvas3D())); } // MsgUpdateConfig will show after the notificaation is clicked