1 #ifndef slic3r_GUI_NotificationManager_hpp_ 2 #define slic3r_GUI_NotificationManager_hpp_ 3 4 #include "Event.hpp" 5 #include "I18N.hpp" 6 7 #include <libslic3r/ObjectID.hpp> 8 #include <libslic3r/Technologies.hpp> 9 10 #include <wx/time.h> 11 12 #include <string> 13 #include <vector> 14 #include <deque> 15 #include <unordered_set> 16 17 namespace Slic3r { 18 namespace GUI { 19 20 using EjectDriveNotificationClickedEvent = SimpleEvent; 21 wxDECLARE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClickedEvent); 22 using ExportGcodeNotificationClickedEvent = SimpleEvent; 23 wxDECLARE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent); 24 using PresetUpdateAvailableClickedEvent = SimpleEvent; 25 wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent); 26 27 class GLCanvas3D; 28 class ImGuiWrapper; 29 30 enum class NotificationType 31 { 32 CustomNotification, 33 // Notification on end of slicing and G-code processing (the full G-code preview is available). 34 // Contains a hyperlink to export the G-code to a removable media. 35 SlicingComplete, 36 // SlicingNotPossible, 37 // Notification on end of export to a removable media, with hyperling to eject the external media. 38 // Obsolete by ExportFinished 39 // ExportToRemovableFinished, 40 // Notification on end of export, with hyperling to see folder and eject if export was to external media. 41 // Own subclass. 42 ExportFinished, 43 // Works on OSX only. 44 //FIXME Do we want to have it on Linux and Windows? Is it possible to get the Disconnect event on Windows? 45 Mouse3dDisconnected, 46 // Mouse3dConnected, 47 // NewPresetsAviable, 48 // Notification on the start of PrusaSlicer, when a new PrusaSlicer version is published. 49 // Contains a hyperlink to open a web browser pointing to the PrusaSlicer download location. 50 NewAppAvailable, 51 // Notification on the start of PrusaSlicer, when updates of system profiles are detected. 52 // Contains a hyperlink to execute installation of the new system profiles. 53 PresetUpdateAvailable, 54 // LoadingFailed, 55 // Not used - instead Slicing error is used for both slicing and validate errors. 56 // ValidateError, 57 // Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background 58 // thread thowing a SlicingError exception. 59 SlicingError, 60 // Slicing warnings, issued by the slicing process. 61 // Slicing warnings are registered for a particular Print milestone or a PrintObject and its milestone. 62 SlicingWarning, 63 // Object partially outside the print volume. Cannot slice. 64 PlaterError, 65 // Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled. 66 PlaterWarning, 67 // Progress bar instead of text. 68 ProgressBar, 69 // Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider. 70 EmptyColorChangeCode, 71 // Notification that custom supports/seams were deleted after mesh repair. 72 CustomSupportsAndSeamRemovedAfterRepair 73 }; 74 75 class NotificationManager 76 { 77 public: 78 enum class NotificationLevel : int 79 { 80 // The notifications will be presented in the order of importance, thus these enum values 81 // are sorted by the importance. 82 // "Good to know" notification, usually but not always with a quick fade-out. 83 RegularNotification = 1, 84 // Information notification without a fade-out or with a longer fade-out. 85 ImportantNotification, 86 // Important notification with progress bar, no fade-out, might appear again after closing. 87 ProgressBarNotification, 88 // Warning, no fade-out. 89 WarningNotification, 90 // Error, no fade-out. 91 ErrorNotification, 92 }; 93 94 NotificationManager(wxEvtHandler* evt_handler); 95 96 // Push a prefabricated notification from basic_notifications (see the table at the end of this file). 97 void push_notification(const NotificationType type, int timestamp = 0); 98 // Push a NotificationType::CustomNotification with NotificationLevel::RegularNotification and 10s fade out interval. 99 void push_notification(const std::string& text, int timestamp = 0); 100 // Push a NotificationType::CustomNotification with provided notification level and 10s for RegularNotification. 101 // ErrorNotification and ImportantNotification are never faded out. 102 void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "", 103 std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>(), int timestamp = 0); 104 // Creates Slicing Error notification with a custom text and no fade out. 105 void push_slicing_error_notification(const std::string& text); 106 // Creates Slicing Warning notification with a custom text and no fade out. 107 void push_slicing_warning_notification(const std::string& text, bool gray, ObjectID oid, int warning_step); 108 // marks slicing errors as gray 109 void set_all_slicing_errors_gray(bool g); 110 // marks slicing warings as gray 111 void set_all_slicing_warnings_gray(bool g); 112 // void set_slicing_warning_gray(const std::string& text, bool g); 113 // immediately stops showing slicing errors 114 void close_slicing_errors_and_warnings(); 115 // Release those slicing warnings, which refer to an ObjectID, which is not in the list. 116 // living_oids is expected to be sorted. 117 void remove_slicing_warnings_of_released_objects(const std::vector<ObjectID>& living_oids); 118 // Object partially outside of the printer working space, cannot print. No fade out. 119 void push_plater_error_notification(const std::string& text); 120 // Object fully out of the printer working space and such. No fade out. 121 void push_plater_warning_notification(const std::string& text); 122 // Closes error or warning of the same text 123 void close_plater_error_notification(const std::string& text); 124 void close_plater_warning_notification(const std::string& text); 125 // Creates special notification slicing complete. 126 // If large = true (Plater side bar is closed), then printing time and export button is shown 127 // at the notification and fade-out is disabled. Otherwise the fade out time is set to 10s. 128 void push_slicing_complete_notification(int timestamp, bool large); 129 // Add a print time estimate to an existing SlicingComplete notification. 130 void set_slicing_complete_print_time(const std::string &info); 131 // Called when the side bar changes its visibility, as the "slicing complete" notification supplements 132 // the "slicing info" normally shown at the side bar. 133 void set_slicing_complete_large(bool large); 134 // Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button 135 void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable); 136 // notification with progress bar 137 void push_progress_bar_notification(const std::string& text, float percentage = 0); 138 void set_progress_bar_percentage(const std::string& text, float percentage); 139 // Close old notification ExportFinished. 140 void new_export_began(bool on_removable); 141 // finds ExportFinished notification and closes it if it was to removable device 142 void device_ejected(); 143 // renders notifications in queue and deletes expired ones 144 void render_notifications(float overlay_width); 145 // finds and closes all notifications of given type 146 void close_notification_of_type(const NotificationType type); 147 // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. 148 void set_in_preview(bool preview); 149 // Move to left to avoid colision with variable layer height gizmo. set_move_from_overlay(bool move)150 void set_move_from_overlay(bool move) { m_move_from_overlay = move; } 151 /* 152 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 153 154 bool requires_update() const { return m_requires_update; } 155 bool requires_render() const { return m_requires_render; } 156 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 157 */ 158 private: 159 // duration 0 means not disapearing 160 struct NotificationData { 161 NotificationType type; 162 NotificationLevel level; 163 // Fade out time 164 const int duration; 165 const std::string text1; 166 const std::string hypertext; 167 // Callback for hypertext - returns true if notification should close after triggering 168 // Usually sends event to UI thread thru wxEvtHandler. 169 // Examples in basic_notifications. 170 std::function<bool(wxEvtHandler*)> callback; 171 const std::string text2; 172 }; 173 174 // Cache of IDs to identify and reuse ImGUI windows. 175 class NotificationIDProvider 176 { 177 public: 178 int allocate_id(); 179 void release_id(int id); 180 181 private: 182 // Next ID used for naming the ImGUI windows. 183 int m_next_id{ 1 }; 184 // IDs of ImGUI windows, which were released and they are ready for reuse. 185 std::vector<int> m_released_ids; 186 }; 187 188 //Pop notification - shows only once to user. 189 class PopNotification 190 { 191 public: 192 193 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 194 enum class EState 195 { 196 Unknown, 197 Hidden, 198 FadingOutRender, // Requesting Render 199 FadingOutStatic, 200 ClosePending, // Requesting Render 201 Finished, // Requesting Render 202 }; 203 #else 204 enum class RenderResult 205 { 206 Finished, 207 ClosePending, 208 Static, 209 Countdown, 210 Hovered 211 }; 212 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 213 214 PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); ~PopNotification()215 virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } 216 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 217 void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); 218 #else 219 RenderResult render(GLCanvas3D& canvas, const float& initial_y, bool move_from_overlay, float overlay_width); 220 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 221 // close will dissapear notification on next render close()222 void close() { m_close_pending = true; } 223 // data from newer notification of same type 224 void update(const NotificationData& n); is_finished() const225 bool is_finished() const { return m_finished || m_close_pending; } 226 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT is_hovered() const227 bool is_hovered() const { return m_hovered; } 228 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 229 // returns top after movement get_top() const230 float get_top() const { return m_top_y; } 231 //returns top in actual frame get_current_top() const232 float get_current_top() const { return m_top_y; } get_type() const233 const NotificationType get_type() const { return m_data.type; } get_data() const234 const NotificationData& get_data() const { return m_data; } is_gray() const235 const bool is_gray() const { return m_is_gray; } 236 // Call equals one second down substract_remaining_time(int seconds)237 void substract_remaining_time(int seconds) { m_remaining_time -= seconds; } set_gray(bool g)238 void set_gray(bool g) { m_is_gray = g; } set_paused(bool p)239 void set_paused(bool p) { m_paused = p; } 240 bool compare_text(const std::string& text); hide(bool h)241 void hide(bool h) { m_hidden = h; } 242 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 243 // sets m_next_render with time of next mandatory rendering 244 void update_state(); next_render() const245 int64_t next_render() const { return m_next_render; } 246 /* 247 bool requires_render() const { return m_state == EState::FadingOutRender || m_state == EState::ClosePending || m_state == EState::Finished; } 248 bool requires_update() const { return m_state != EState::Hidden; } 249 */ get_state() const250 EState get_state() const { return m_state; } 251 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 252 protected: 253 // Call after every size change 254 void init(); 255 // Part of init() 256 virtual void count_spaces(); 257 // Calculetes correct size but not se it in imgui! 258 virtual void set_next_window_size(ImGuiWrapper& imgui); 259 virtual void render_text(ImGuiWrapper& imgui, 260 const float win_size_x, const float win_size_y, 261 const float win_pos_x , const float win_pos_y); 262 virtual void render_close_button(ImGuiWrapper& imgui, 263 const float win_size_x, const float win_size_y, 264 const float win_pos_x , const float win_pos_y); 265 #if !ENABLE_NEW_NOTIFICATIONS_FADE_OUT 266 void render_countdown(ImGuiWrapper& imgui, 267 const float win_size_x, const float win_size_y, 268 const float win_pos_x , const float win_pos_y); 269 #endif // !ENABLE_NEW_NOTIFICATIONS_FADE_OUT 270 virtual void render_hypertext(ImGuiWrapper& imgui, 271 const float text_x, const float text_y, 272 const std::string text, 273 bool more = false); 274 // Left sign could be error or warning sign 275 void render_left_sign(ImGuiWrapper& imgui); 276 virtual void render_minimize_button(ImGuiWrapper& imgui, 277 const float win_pos_x, const float win_pos_y); 278 // Hypertext action, returns true if notification should close. 279 // Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)> 280 virtual bool on_text_click(); 281 282 const NotificationData m_data; 283 284 // For reusing ImGUI windows. 285 NotificationIDProvider &m_id_provider; 286 287 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 288 EState m_state { EState::Unknown }; 289 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 290 291 int m_id { 0 }; 292 bool m_initialized { false }; 293 // Main text 294 std::string m_text1; 295 // Clickable text 296 std::string m_hypertext; 297 // Aditional text after hypertext - currently not used 298 std::string m_text2; 299 // Countdown variables 300 long m_remaining_time; 301 bool m_counting_down; 302 long m_last_remaining_time; 303 bool m_paused { false }; 304 int m_countdown_frame { 0 }; 305 bool m_fading_out { false }; 306 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 307 int64_t m_fading_start { 0LL }; 308 // time of last done render when fading 309 int64_t m_last_render_fading { 0LL }; 310 // first appereance of notification or last hover; 311 int64_t m_notification_start; 312 // time to next must-do render 313 int64_t m_next_render { std::numeric_limits<int64_t>::max() }; 314 #else 315 // total time left when fading beggins 316 float m_fading_time{ 0.0f }; 317 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 318 float m_current_fade_opacity { 1.0f }; 319 // If hidden the notif is alive but not visible to user 320 bool m_hidden { false }; 321 // m_finished = true - does not render, marked to delete 322 bool m_finished { false }; 323 // Will go to m_finished next render 324 bool m_close_pending { false }; 325 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 326 bool m_hovered { false }; 327 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 328 // variables to count positions correctly 329 // all space without text 330 float m_window_width_offset; 331 // Space on left side without text 332 float m_left_indentation; 333 // Total size of notification window - varies based on monitor 334 float m_window_height { 56.0f }; 335 float m_window_width { 450.0f }; 336 //Distance from bottom of notifications to top of this notification 337 float m_top_y { 0.0f }; 338 339 // Height of text 340 // Used as basic scaling unit! 341 float m_line_height; 342 std::vector<int> m_endlines; 343 // Gray are f.e. eorrors when its uknown if they are still valid 344 bool m_is_gray { false }; 345 //if multiline = true, notification is showing all lines(>2) 346 bool m_multiline { false }; 347 // True if minimized button is rendered, helps to decide where is area for invisible close button 348 bool m_minimize_b_visible { false }; 349 int m_lines_count{ 1 }; 350 // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. 351 wxEvtHandler* m_evt_handler; 352 }; 353 354 class SlicingCompleteLargeNotification : public PopNotification 355 { 356 public: 357 SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds); 358 void set_large(bool l); get_large()359 bool get_large() { return m_is_large; } 360 361 void set_print_info(const std::string &info); 362 protected: 363 virtual void render_text(ImGuiWrapper& imgui, 364 const float win_size_x, const float win_size_y, 365 const float win_pos_x, const float win_pos_y) 366 override; 367 bool m_is_large; 368 bool m_has_print_info { false }; 369 std::string m_print_info { std::string() }; 370 }; 371 372 class SlicingWarningNotification : public PopNotification 373 { 374 public: SlicingWarningNotification(const NotificationData & n,NotificationIDProvider & id_provider,wxEvtHandler * evt_handler)375 SlicingWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} 376 ObjectID object_id; 377 int warning_step; 378 }; 379 380 class ProgressBarNotification : public PopNotification 381 { 382 public: ProgressBarNotification(const NotificationData & n,NotificationIDProvider & id_provider,wxEvtHandler * evt_handler,float percentage)383 ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); } set_percentage(float percent)384 void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; } 385 protected: 386 void init(); 387 void render_text(ImGuiWrapper& imgui, 388 const float win_size_x, const float win_size_y, 389 const float win_pos_x, const float win_pos_y) override; 390 void render_bar(ImGuiWrapper& imgui, 391 const float win_size_x, const float win_size_y, 392 const float win_pos_x, const float win_pos_y); 393 bool m_progress_complete{ false }; 394 float m_percentage; 395 }; 396 397 class ExportFinishedNotification : public PopNotification 398 { 399 public: ExportFinishedNotification(const NotificationData & n,NotificationIDProvider & id_provider,wxEvtHandler * evt_handler,bool to_removable,const std::string & export_path,const std::string & export_dir_path)400 ExportFinishedNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool to_removable,const std::string& export_path,const std::string& export_dir_path) 401 : PopNotification(n, id_provider, evt_handler) 402 , m_to_removable(to_removable) 403 , m_export_path(export_path) 404 , m_export_dir_path(export_dir_path) 405 { 406 m_multiline = true; 407 } 408 bool m_to_removable; 409 std::string m_export_path; 410 std::string m_export_dir_path; 411 protected: 412 // Reserves space on right for more buttons 413 void count_spaces() override; 414 void render_text(ImGuiWrapper& imgui, 415 const float win_size_x, const float win_size_y, 416 const float win_pos_x, const float win_pos_y) override; 417 // Renders also button to open directory with exported path and eject removable media 418 void render_close_button(ImGuiWrapper& imgui, 419 const float win_size_x, const float win_size_y, 420 const float win_pos_x, const float win_pos_y) override; 421 void render_eject_button(ImGuiWrapper& imgui, 422 const float win_size_x, const float win_size_y, 423 const float win_pos_x, const float win_pos_y); render_minimize_button(ImGuiWrapper & imgui,const float win_pos_x,const float win_pos_y)424 void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override 425 { m_minimize_b_visible = false; } 426 bool on_text_click() override; 427 // local time of last hover for showing tooltip 428 long m_hover_time { 0 }; 429 }; 430 431 //pushes notification into the queue of notifications that are rendered 432 //can be used to create custom notification 433 bool push_notification_data(const NotificationData& notification_data, int timestamp); 434 bool push_notification_data(std::unique_ptr<NotificationManager::PopNotification> notification, int timestamp); 435 //finds older notification of same type and moves it to the end of queue. returns true if found 436 bool activate_existing(const NotificationManager::PopNotification* notification); 437 // Put the more important notifications to the bottom of the list. 438 void sort_notifications(); 439 // If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed. 440 bool has_slicing_error_notification(); 441 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 442 // perform update_state on each notification and ask for more frames if needed 443 void update_notifications(); 444 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 445 446 // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. 447 wxEvtHandler* m_evt_handler; 448 // Cache of IDs to identify and reuse ImGUI windows. 449 NotificationIDProvider m_id_provider; 450 std::deque<std::unique_ptr<PopNotification>> m_pop_notifications; 451 // Last render time in seconds for fade out control. 452 long m_last_time { 0 }; 453 // When mouse hovers over some notification, the fade-out of all notifications is suppressed. 454 bool m_hovered { false }; 455 //timestamps used for slicing finished - notification could be gone so it needs to be stored here 456 std::unordered_set<int> m_used_timestamps; 457 // True if G-code preview is active. False if the Plater is active. 458 bool m_in_preview { false }; 459 // True if the layer editing is enabled in Plater, so that the notifications are shifted left of it. 460 bool m_move_from_overlay { false }; 461 /* 462 #if ENABLE_NEW_NOTIFICATIONS_FADE_OUT 463 bool m_requires_update{ false }; 464 bool m_requires_render{ false }; 465 #endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT 466 */ 467 //prepared (basic) notifications 468 static const NotificationData basic_notifications[]; 469 }; 470 471 }//namespace GUI 472 }//namespace Slic3r 473 474 #endif //slic3r_GUI_NotificationManager_hpp_ 475