Notifications management and rendering refactoring.

With warning notification Model out of bed reworked to not show after dismiss.
This commit is contained in:
David Kocik 2021-02-08 16:14:35 +01:00
parent 08a826d237
commit c41df487bb
6 changed files with 293 additions and 324 deletions

View File

@ -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<const ConfigOptionPoints*>(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<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(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)

View File

@ -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);

View File

@ -641,6 +641,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
error = true;
break;
}
BOOST_LOG_TRIVIAL(error) << state << " : " << text ;
auto &notification_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<int>::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<bool>(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<wxWindow*>(wxGetApp().plater());
while (p->GetParent() != nullptr)
p = p->GetParent();
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(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);

View File

@ -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); }

View File

@ -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();
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);
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<int64_t>::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;
if (m_state != EState::FadingOut && up_time >= m_data.duration * 1000) {
m_state = EState::FadingOut;
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<int64_t>(/*m_data.duration * 1000 - up_time*/m_remaining_time * 1000, MAX_TIMEOUT_MILISECONDS);
} else if (m_state != EState::FadingOut) {
m_next_render = m_data.duration * 1000 - up_time;
}
}
// 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<float>(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_finished) {
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<float>(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<PopNotification> &notification : 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<PopNotification>& 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<PlaterWarningNotification*>(notification.get())->show();
return;
}
}
}
NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
auto notification = std::make_unique<NotificationManager::PlaterWarningNotification>(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<PopNotification> &notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
notification->close();
dynamic_cast<PlaterWarningNotification*>(notification.get())->real_close();
}
}
}
@ -1008,7 +983,8 @@ void NotificationManager::set_progress_bar_percentage(const std::string& text, f
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) {
dynamic_cast<ProgressBarNotification*>(notification.get())->set_percentage(percentage);
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_ptr<NotificationMan
if (this->activate_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<wxWindow*>(wxGetApp().plater());
while (p->GetParent() != nullptr)
p = p->GetParent();
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(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<int64_t>::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<PopNotification>& 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<PopNotification>& notification = *it;
notification->update_state(hover, time_since_render);
next_render = std::min<int64_t>(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<wxWindow*>(wxGetApp().plater());
while (p->GetParent() != nullptr)
p = p->GetParent();
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(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<int64_t>::max();
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
std::unique_ptr<PopNotification>& notification = *it;
notification->set_paused(m_hovered);
notification->update_state();
next_render = std::min<int64_t>(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<PopNotification>& notification : m_pop_notifications) {
if (notification->requires_update()) {
m_requires_update = true;
break;
}
}
*/
// update hovering state
m_hovered = false;
for (const std::unique_ptr<PopNotification>& 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<PopNotification>& 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<int64_t>::max())
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
/*
// actualizate timers
wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
while (p->GetParent() != nullptr)
p = p->GetParent();
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(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) {

View File

@ -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; }
*/
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; }
protected:
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<bool(wxEvtHandler*)>
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<int64_t>::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<int64_t>::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<size_t> 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<std::unique_ptr<PopNotification>> 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<int> 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<NotificationData> 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));