New Export Finished notification showing path and opening containing folder. Fix of #4917. Fixed wrongly grayed eject button in File menu. Hopefully fix of ctrl shortcut of tooltips at sidebar.

This commit is contained in:
David Kocik 2020-11-04 09:23:47 +01:00
parent 729304c129
commit 3ca3a544a8
13 changed files with 552 additions and 92 deletions

View file

@ -53,6 +53,24 @@ bool SlicingProcessCompletedEvent::critical_error() const
return true;
}
bool SlicingProcessCompletedEvent::invalidate_plater() const
{
if (critical_error())
{
try {
this->rethrow_exception();
}
catch (const Slic3r::ExportError&) {
// Exception thrown by copying file does not ivalidate plater
return false;
}
catch (...) {
}
return true;
}
return false;
}
std::string SlicingProcessCompletedEvent::format_error_message() const
{
std::string error;
@ -142,19 +160,19 @@ void BackgroundSlicingProcess::process_fff()
switch (copy_ret_val) {
case SUCCESS: break; // no error
case FAIL_COPY_FILE:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?\nError message: %1%"))) % error_message).str());
break;
case FAIL_FILES_DIFFERENT:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at %1%.tmp."))) % export_path).str());
break;
case FAIL_RENAMING:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
throw Slic3r::ExportError((boost::format(_utf8(L("Renaming of the G-code after copying to the selected destination folder has failed. Current path is %1%.tmp. Please try exporting again."))) % export_path).str());
break;
case FAIL_CHECK_ORIGIN_NOT_OPENED:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str());
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the original code at %1% couldn't be opened during copy check. The output G-code is at %2%.tmp."))) % m_temp_output_path % export_path).str());
break;
case FAIL_CHECK_TARGET_NOT_OPENED:
throw Slic3r::RuntimeError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
throw Slic3r::ExportError((boost::format(_utf8(L("Copying of the temporary G-code has finished but the exported code couldn't be opened during copy check. The output G-code is at %1%.tmp."))) % export_path).str());
break;
default:
throw Slic3r::RuntimeError(_utf8(L("Unknown error occured during exporting G-code.")));

View file

@ -57,6 +57,8 @@ public:
bool error() const { return m_status == Error; }
// Unhandled error produced by stdlib or a Win32 structured exception, or unhandled Slic3r's own critical exception.
bool critical_error() const;
// Critical errors does invalidate plater except CopyFileError.
bool invalidate_plater() const;
// Only valid if error()
void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
// Produce a human readable message to be displayed by a notification or a message box.

View file

@ -49,7 +49,9 @@ static const std::map<const char, std::string> font_icons = {
{ImGui::MinimalizeMarker , "notification_minimalize" },
{ImGui::MinimalizeHoverMarker , "notification_minimalize_hover" },
{ImGui::WarningMarker , "notification_warning" },
{ImGui::ErrorMarker , "notification_error" }
{ImGui::ErrorMarker , "notification_error" },
{ImGui::EjectMarker , "notification_eject_sd" },
{ImGui::EjectHoverMarker , "notification_eject_sd_hover" },
};
const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };

View file

@ -1,6 +1,7 @@
#include "NotificationManager.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "Plater.hpp"
#include "GLCanvas3D.hpp"
#include "ImGuiWrapper.hpp"
@ -33,6 +34,56 @@ namespace Notifications_Internal{
else
ImGui::PushStyleColor(idx, col);
}
void open_folder(const std::string& path)
{
// Code taken from desktop_open_datadir_folder()
// Execute command to open a file explorer, platform dependent.
// FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade.
#ifdef _WIN32
const wxString widepath = from_u8(path);
const wchar_t* argv[] = { L"explorer", widepath.GetData(), nullptr };
::wxExecute(const_cast<wchar_t**>(argv), wxEXEC_ASYNC, nullptr);
#elif __APPLE__
const char* argv[] = { "open", path.data(), nullptr };
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr);
#else
const char* argv[] = { "xdg-open", path.data(), nullptr };
// Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars,
// because they may mess up the environment expected by the file manager.
// Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure.
if (wxGetEnv("APPIMAGE", nullptr)) {
// We're running from AppImage
wxEnvVariableHashMap env_vars;
wxGetEnvMap(&env_vars);
env_vars.erase("APPIMAGE");
env_vars.erase("APPDIR");
env_vars.erase("LD_LIBRARY_PATH");
env_vars.erase("LD_PRELOAD");
env_vars.erase("UNION_PRELOAD");
wxExecuteEnv exec_env;
exec_env.env = std::move(env_vars);
wxString owd;
if (wxGetEnv("OWD", &owd)) {
// This is the original work directory from which the AppImage image was run,
// set it as CWD for the child process:
exec_env.cwd = std::move(owd);
}
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, &exec_env);
}
else {
// Looks like we're NOT running from AppImage, we'll make no changes to the environment.
::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, nullptr);
}
#endif
}
}
#if 1
@ -183,6 +234,7 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif
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);
m_minimize_b_visible = false;
if (m_multiline && m_lines_count > 3)
render_minimize_button(imgui, win_pos.x, win_pos.y);
} else {
@ -205,12 +257,8 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif
ImGui::PopStyleColor();
return ret_val;
}
void NotificationManager::PopNotification::init()
void NotificationManager::PopNotification::count_spaces()
{
std::string text = m_text1 + " " + m_hypertext;
int last_end = 0;
m_lines_count = 0;
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
@ -221,8 +269,16 @@ void NotificationManager::PopNotification::init()
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
}
m_window_width_offset = m_left_indentation + m_line_height * 2;
m_window_width_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;
int last_end = 0;
m_lines_count = 0;
count_spaces();
// count lines
m_endlines.clear();
@ -233,10 +289,9 @@ void NotificationManager::PopNotification::init()
//next line is ended by '/n'
m_endlines.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
} else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - 3.5f * m_line_height) {// m_window_width_offset) {
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
int next_space = text.find_first_of(' ', last_end);
if (next_space > 0) {
@ -245,8 +300,19 @@ void NotificationManager::PopNotification::init()
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
m_endlines.push_back(next_space);
last_end = next_space + 1;
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
letter_count++;
}
m_endlines.push_back(last_end + letter_count);
last_end += letter_count;
} else {
m_endlines.push_back(next_space);
last_end = next_space + 1;
}
}
}
else {
@ -257,6 +323,8 @@ void NotificationManager::PopNotification::init()
}
m_lines_count++;
}
if (m_lines_count == 3)
m_multiline = true;
m_initialized = true;
}
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
@ -285,7 +353,8 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
float shift_y = m_line_height;// -m_line_height / 20;
for (size_t i = 0; i < m_lines_count; i++) {
std::string line = m_text1.substr(last_end , m_endlines[i] - last_end);
last_end = m_endlines[i] + 1;
if(i < m_lines_count - 1)
last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
imgui.text(line.c_str());
@ -303,7 +372,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
// line2
std::string line = m_text1.substr(m_endlines[0] + 1, m_endlines[1] - m_endlines[0] - 1);
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x)
{
line = line.substr(0, line.length() - 6);
@ -326,7 +395,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
// line2
std::string line = m_text1.substr(m_endlines[0] + 1);
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(cursor_y);
@ -375,8 +444,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
set_next_window_size(imgui);
}
else {
on_text_click();
m_close_pending = true;
m_close_pending = on_text_click();
}
}
ImGui::PopStyleColor();
@ -407,7 +475,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
orange_color.w = 0.8f;
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
@ -422,7 +490,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
button_text = ImGui::CloseIconMarker;
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
ImVec2(win_pos.x, win_pos.y + win_size.y - (m_multiline? 2 * m_line_height : 0)),
ImVec2(win_pos.x, win_pos.y + win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0)),
true))
{
button_text = ImGui::CloseIconHoverMarker;
@ -435,11 +503,10 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
{
m_close_pending = true;
}
//invisible large button
ImGui::SetCursorPosX(win_size.x - win_size.x / 10.f);
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.125);
ImGui::SetCursorPosY(0);
if (imgui.button(" ", win_size.x / 10.f, win_size.y - (m_multiline ? 2 * m_line_height : 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;
}
@ -540,15 +607,12 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
m_minimize_b_visible = true;
}
void NotificationManager::PopNotification::on_text_click()
bool NotificationManager::PopNotification::on_text_click()
{
bool ret = true;
switch (m_data.type) {
case NotificationType::ExportToRemovableFinished :
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
break;
case NotificationType::SlicingComplete :
//wxGetApp().plater()->export_gcode(false);
assert(m_evt_handler != nullptr);
@ -567,6 +631,7 @@ void NotificationManager::PopNotification::on_text_click()
default:
break;
}
return ret;
}
void NotificationManager::PopNotification::update(const NotificationData& n)
{
@ -633,6 +698,127 @@ void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
m_hypertext = l ? _u8L("Export G-Code.") : std::string();
m_hidden = !l;
}
//---------------ExportFinishedNotification-----------
void NotificationManager::ExportFinishedNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
m_left_indentation = m_line_height;
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
std::string text;
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
}
//TODO count this properly
m_window_width_offset = m_left_indentation + m_line_height * (m_to_removable ? 5.f : 3.f);
m_window_width = m_line_height * 25;
}
void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
float x_offset = m_left_indentation;
std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
// Lines are always at least two and m_multiline is always true for ExportFinishedNotification.
// First line has "Export Finished" text and than hyper text open folder.
// Following lines are path to gcode.
int last_end = 0;
float starting_y = m_line_height / 2;//10;
float shift_y = m_line_height;// -m_line_height / 20;
for (size_t i = 0; i < m_lines_count; i++) {
std::string line = m_text1.substr(last_end, m_endlines[i] - last_end);
if (i < m_lines_count - 1)
last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
imgui.text(line.c_str());
//hyperlink text
if ( i == 0 ) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(m_text1.substr(0, last_end).c_str()).x + ImGui::CalcTextSize(" ").x, starting_y, _u8L("Open Folder."));
}
}
}
void NotificationManager::ExportFinishedNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
PopNotification::render_close_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
if(m_to_removable)
render_eject_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
}
void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
orange_color.w = 0.8f;
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::EjectMarker;
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 4.5f, win_pos.y),
ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y),
true))
{
button_text = ImGui::EjectHoverMarker;
// tooltip
long time_now = wxGetLocalTime();
if (m_hover_time > 0 && m_hover_time < time_now) {
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
ImGui::BeginTooltip();
imgui.text(_u8L("Eject drive"));
ImGui::EndTooltip();
ImGui::PopStyleColor();
}
if (m_hover_time == 0)
m_hover_time = time_now;
} else
m_hover_time = 0;
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosX(win_size.x - m_line_height * 4.f);
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y / 2);
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
m_close_pending = true;
}
//invisible large button
ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f);
ImGui::SetCursorPosY(0);
if (imgui.button(" ", m_line_height * 2.f, win_size.y))
{
assert(m_evt_handler != nullptr);
if (m_evt_handler != nullptr)
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
m_close_pending = true;
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
bool NotificationManager::ExportFinishedNotification::on_text_click()
{
Notifications_Internal::open_folder(m_export_dir_path);
return false;
}
//------NotificationManager--------
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
m_evt_handler(evt_handler)
@ -789,6 +975,13 @@ void NotificationManager::remove_slicing_warnings_of_released_objects(const std:
notification->close();
}
}
void NotificationManager::push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable)
{
close_notification_of_type(NotificationType::ExportFinished);
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, 0, _u8L("Exporting finished.") +"\n"+ path };
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path),
canvas, 0);
}
bool NotificationManager::push_notification_data(const NotificationData &notification_data, GLCanvas3D& canvas, int timestamp)
{
return push_notification_data(std::make_unique<PopNotification>(notification_data, m_id_provider, m_evt_handler), canvas, timestamp);
@ -822,7 +1015,7 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay
bool hovered = false;
sort_notifications();
// iterate thru notifications and render them / erease them
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
if ((*it)->get_finished()) {
it = m_pop_notifications.erase(it);
} else {
@ -931,5 +1124,31 @@ bool NotificationManager::has_slicing_error_notification()
});
}
void NotificationManager::new_export_began(bool on_removable)
{
close_notification_of_type(NotificationType::ExportFinished);
// If we want to hold information of ejecting removable on later export finished notifications
/*
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::ExportToRemovableFinished) {
if (!on_removable) {
const NotificationData old_data = notification->get_data();
notification->update( {old_data.type, old_data.level ,old_data.duration, std::string(), old_data.hypertext} );
} else {
notification->close();
}
return;
}
}
*/
}
void NotificationManager::device_ejected()
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::ExportFinished && dynamic_cast<ExportFinishedNotification*>(notification.get())->m_to_removable)
notification->close();
}
}
}//namespace GUI
}//namespace Slic3r

View file

@ -32,7 +32,11 @@ enum class NotificationType
SlicingComplete,
// SlicingNotPossible,
// Notification on end of export to a removable media, with hyperling to eject the external media.
ExportToRemovableFinished,
// Obsolete by ExportFinished
// ExportToRemovableFinished,
// Notification on end of export, with hyperling to see folder and eject if export was to external media.
// Own subclass.
ExportFinished,
// Works on OSX only.
//FIXME Do we want to have it on Linux and Windows? Is it possible to get the Disconnect event on Windows?
Mouse3dDisconnected,
@ -115,15 +119,21 @@ public:
// Called when the side bar changes its visibility, as the "slicing complete" notification supplements
// the "slicing info" normally shown at the side bar.
void set_slicing_complete_large(bool large);
// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
void push_exporting_finished_notification(GLCanvas3D& canvas, std::string path, std::string dir_path, bool on_removable);
// Close old notification ExportFinished.
void new_export_began(bool on_removable);
// 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(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
// Move to left to avoid colision with variable layer height gizmo.
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
private:
// duration 0 means not disapearing
struct NotificationData {
@ -169,7 +179,7 @@ private:
void close() { m_close_pending = true; }
// data from newer notification of same type
void update(const NotificationData& n);
bool get_finished() const { return m_finished; }
bool get_finished() const { return m_finished || m_close_pending; }
// returns top after movement
float get_top() const { return m_top_y; }
//returns top in actual frame
@ -187,25 +197,29 @@ private:
protected:
// Call after every size change
void init();
// Part of init()
virtual void count_spaces();
// Calculetes correct size but not se it in imgui!
virtual void set_next_window_size(ImGuiWrapper& imgui);
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x , const float win_pos_y);
void render_close_button(ImGuiWrapper& imgui,
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x , const float win_pos_y);
void render_countdown(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x , const float win_pos_y);
void render_hypertext(ImGuiWrapper& imgui,
virtual void render_hypertext(ImGuiWrapper& imgui,
const float text_x, const float text_y,
const std::string text,
bool more = false);
// Left sign could be error or warning sign
void render_left_sign(ImGuiWrapper& imgui);
void render_minimize_button(ImGuiWrapper& imgui,
virtual void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y);
void on_text_click();
// Hypertext action, returns if close notification
virtual bool on_text_click();
const NotificationData m_data;
@ -236,7 +250,9 @@ private:
// Will go to m_finished next render
bool m_close_pending { false };
// variables to count positions correctly
// all space without text
float m_window_width_offset;
// Space on left side without text
float m_left_indentation;
// Total size of notification window - varies based on monitor
float m_window_height { 56.0f };
@ -252,6 +268,8 @@ private:
bool m_is_gray { false };
//if multiline = true, notification is showing all lines(>2)
bool m_multiline { false };
// True if minimized button is rendered, helps to decide where is area for invisible close button
bool m_minimize_b_visible { false };
int m_lines_count{ 1 };
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
wxEvtHandler* m_evt_handler;
@ -270,7 +288,6 @@ private:
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
override;
bool m_is_large;
bool m_has_print_info { false };
std::string m_print_info { std::string() };
@ -284,6 +301,40 @@ private:
int warning_step;
};
class ExportFinishedNotification : public PopNotification
{
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)
: PopNotification(n, id_provider, evt_handler)
, m_to_removable(to_removable)
, m_export_path(export_path)
, m_export_dir_path(export_dir_path)
{
m_multiline = true;
}
bool m_to_removable;
std::string m_export_path;
std::string m_export_dir_path;
protected:
// Reserves space on right for more buttons
virtual void count_spaces() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
// Renders also button to open directory with exported path and eject removable media
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_eject_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
{ m_minimize_b_visible = false; }
virtual bool on_text_click() override;
// local time of last hover for showing tooltip
long m_hover_time { 0 };
};
//pushes notification into the queue of notifications that are rendered
//can be used to create custom notification
bool push_notification_data(const NotificationData& notification_data, GLCanvas3D& canvas, int timestamp);
@ -314,7 +365,7 @@ private:
//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::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.") },

View file

@ -578,7 +578,7 @@ struct Sidebar::priv
wxButton *btn_export_gcode;
wxButton *btn_reslice;
ScalableButton *btn_send_gcode;
ScalableButton *btn_eject_device;
//ScalableButton *btn_eject_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
bool is_collapsed {false};
@ -750,13 +750,14 @@ Sidebar::Sidebar(Plater *parent)
(*btn)->Hide();
};
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + "\tCtrl+Shift+G");
init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device") + "\tCtrl+T");
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + "\tCtrl+U");
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer ") + GUI::shortkey_ctrl_prefix() + "Shift+G");
// init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device ") + GUI::shortkey_ctrl_prefix() + "T");
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive ") + GUI::shortkey_ctrl_prefix() + "U");
// regular buttons "Slice now" and "Export G-code"
const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4;
// const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4;
const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4;
auto init_btn = [this](wxButton **btn, wxString label, const int button_height) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxSize(-1, button_height), wxBU_EXACTFIT);
@ -774,7 +775,7 @@ Sidebar::Sidebar(Plater *parent)
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
complect_btns_sizer->Add(p->btn_export_gcode_removable);
complect_btns_sizer->Add(p->btn_eject_device);
// complect_btns_sizer->Add(p->btn_eject_device);
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
@ -797,7 +798,7 @@ Sidebar::Sidebar(Plater *parent)
p->plater->select_view_3D("Preview");
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
@ -940,9 +941,9 @@ void Sidebar::msw_rescale()
p->object_info->msw_rescale();
p->btn_send_gcode->msw_rescale();
p->btn_eject_device->msw_rescale();
// p->btn_eject_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
const int scaled_height = p->btn_eject_device->GetBitmap().GetHeight() + 4;
const int scaled_height = p->btn_export_gcode_removable->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
@ -965,7 +966,7 @@ void Sidebar::sys_color_changed()
// btn...->msw_rescale() updates icon on button, so use it
p->btn_send_gcode->msw_rescale();
p->btn_eject_device->msw_rescale();
// p->btn_eject_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
p->scrolled->Layout();
@ -1268,7 +1269,7 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
p->btn_eject_device->Enable(enable);
// p->btn_eject_device->Enable(enable);
p->btn_export_gcode_removable->Enable(enable);
}
@ -1276,8 +1277,8 @@ bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Sh
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); }
bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); }
bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); }
//bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); }
//bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); }
bool Sidebar::is_multifilament()
{
@ -1468,6 +1469,13 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
return true;
}
// State to manage showing after export notifications and device ejecting
enum ExportingStatus{
NOT_EXPORTING,
EXPORTING_TO_REMOVABLE,
EXPORTING_TO_LOCAL
};
// Plater / private
struct Plater::priv
{
@ -1770,8 +1778,9 @@ struct Plater::priv
// Caching last value of show_action_buttons parameter for show_action_buttons(), so that a callback which does not know this state will not override it.
mutable bool ready_to_slice = { false };
// Flag indicating that the G-code export targets a removable device, therefore the show_action_buttons() needs to be called at any case when the background processing finishes.
bool writing_to_removable_device { false };
bool show_ExportToRemovableFinished_notification { false };
ExportingStatus exporting_status { NOT_EXPORTING };
std::string last_output_path;
std::string last_output_dir_path;
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
bool process_completed_with_error { false };
private:
@ -2043,9 +2052,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
});
this->q->Bind(EVT_REMOVABLE_DRIVES_CHANGED, [this, q](RemovableDrivesChangedEvent &) {
this->show_action_buttons(this->ready_to_slice);
if (!this->sidebar->get_eject_shown()) {
notification_manager->close_notification_of_type(NotificationType::ExportToRemovableFinished);
}
// Close notification ExportingFinished but only if last export was to removable
notification_manager->device_ejected();
});
// Start the background thread and register this window as a target for update events.
wxGetApp().removable_drive_manager()->init(this->q);
@ -2912,6 +2920,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
const wxString invalid_str = _L("Invalid data");
for (auto btn : {ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport})
sidebar->set_btn_label(btn, invalid_str);
process_completed_with_error = true;
}
else
{
@ -3511,9 +3520,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent & evt)
void Plater::priv::on_export_began(wxCommandEvent& evt)
{
if (show_warning_dialog)
warnings_dialog();
if (this->writing_to_removable_device)
this->show_ExportToRemovableFinished_notification = true;
warnings_dialog();
}
void Plater::priv::on_slicing_began()
{
@ -3591,10 +3598,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
} else
notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D());
this->statusbar()->set_status_text(from_u8(message));
const wxString invalid_str = _L("Invalid data");
for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport })
sidebar->set_btn_label(btn, invalid_str);
process_completed_with_error = true;
if (evt.invalidate_plater())
{
const wxString invalid_str = _L("Invalid data");
for (auto btn : { ActionButtonType::abReslice, ActionButtonType::abSendGCode, ActionButtonType::abExport })
sidebar->set_btn_label(btn, invalid_str);
process_completed_with_error = true;
}
}
if (evt.cancelled())
this->statusbar()->set_status_text(_L("Cancelled"));
@ -3629,13 +3640,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
show_action_buttons(false);
}
// If writing to removable drive was scheduled, show notification with eject button
if (this->writing_to_removable_device && this->show_ExportToRemovableFinished_notification) {
if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !this->process_completed_with_error) {
show_action_buttons(false);
notification_manager->push_notification(NotificationType::ExportToRemovableFinished, *q->get_current_canvas3D());
}
notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, true);
wxGetApp().removable_drive_manager()->set_exporting_finished(true);
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !this->process_completed_with_error)
notification_manager->push_exporting_finished_notification(*q->get_current_canvas3D(), last_output_path, last_output_dir_path, false);
}
this->show_ExportToRemovableFinished_notification = false;
this->writing_to_removable_device = false;
exporting_status = ExportingStatus::NOT_EXPORTING;
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@ -4306,8 +4318,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
sidebar->show_export_removable(removable_media_status.has_removable_drives) |
sidebar->show_eject(removable_media_status.has_eject))
sidebar->show_export_removable(removable_media_status.has_removable_drives))
// sidebar->show_eject(removable_media_status.has_eject))
sidebar->Layout();
}
else
@ -4318,8 +4330,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
if (sidebar->show_reslice(ready_to_slice) |
sidebar->show_export(!ready_to_slice) |
sidebar->show_send(send_gcode_shown && !ready_to_slice) |
sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives) |
sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject))
sidebar->show_export_removable(!ready_to_slice && removable_media_status.has_removable_drives))
// sidebar->show_eject(!ready_to_slice && removable_media_status.has_eject))
sidebar->Layout();
}
}
@ -4958,7 +4970,7 @@ void Plater::export_gcode(bool prefer_removable)
if (p->model.objects.empty())
return;
if (p->process_completed_with_error)//here
if (p->process_completed_with_error)
return;
// If possible, remove accents from accented latin characters.
@ -5003,7 +5015,10 @@ void Plater::export_gcode(bool prefer_removable)
if (! output_path.empty()) {
bool path_on_removable_media = removable_drive_manager.set_and_verify_last_save_path(output_path.string());
p->writing_to_removable_device = path_on_removable_media;
p->notification_manager->new_export_began(path_on_removable_media);
p->exporting_status = path_on_removable_media ? ExportingStatus::EXPORTING_TO_REMOVABLE : ExportingStatus::EXPORTING_TO_LOCAL;
p->last_output_path = output_path.string();
p->last_output_dir_path = output_path.parent_path().string();
p->export_gcode(output_path, path_on_removable_media, PrintHostJob());
// Storing a path to AppConfig either as path to removable media or a path to internal media.
// is_path_on_removable_drive() is called with the "true" parameter to update its internal database as the user may have shuffled the external drives
@ -5223,6 +5238,10 @@ void Plater::export_toolpaths_to_obj() const
void Plater::reslice()
{
// There is "invalid data" button instead "slice now"
if (p->process_completed_with_error)
return;
// Stop arrange and (or) optimize rotation tasks.
this->stop_jobs();

View file

@ -391,7 +391,6 @@ bool RemovableDriveManager::set_and_verify_last_save_path(const std::string &pat
#ifndef REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
this->update();
#endif // REMOVABLE_DRIVE_MANAGER_OS_CALLBACKS
m_last_save_path = this->get_removable_drive_from_path(path);
m_exporting_finished = false;
return ! m_last_save_path.empty();