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