From 431cfcc671f2cf8b861814e24c5afd5c1c7ec9d7 Mon Sep 17 00:00:00 2001
From: enricoturri1966 <enricoturri@seznam.cz>
Date: Thu, 9 Jul 2020 15:57:35 +0200
Subject: [PATCH] GCodeViewer -> Reworked layout of imgui dialog for estimated
 printing times

---
 src/slic3r/GUI/GCodeViewer.cpp  | 254 +++++++++++++++++++-------------
 src/slic3r/GUI/ImGuiWrapper.cpp |   7 +
 2 files changed, 160 insertions(+), 101 deletions(-)

diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 26dc765db..3ceaf86fa 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -40,23 +40,28 @@ static GCodeProcessor::EMoveType buffer_type(unsigned char id) {
     return static_cast<GCodeProcessor::EMoveType>(static_cast<unsigned char>(GCodeProcessor::EMoveType::Retract) + id);
 }
 
-std::vector<std::array<float, 3>> decode_colors(const std::vector<std::string> & colors) {
+std::array<float, 3> decode_color(const std::string& color) {
     static const float INV_255 = 1.0f / 255.0f;
 
+    std::array<float, 3> ret;
+    const char* c = color.data() + 1;
+    if ((color.size() == 7) && (color.front() == '#')) {
+        for (size_t j = 0; j < 3; ++j) {
+            int digit1 = hex_digit_to_int(*c++);
+            int digit2 = hex_digit_to_int(*c++);
+            if ((digit1 == -1) || (digit2 == -1))
+                break;
+
+            ret[j] = float(digit1 * 16 + digit2) * INV_255;
+        }
+    }
+    return ret;
+}
+
+std::vector<std::array<float, 3>> decode_colors(const std::vector<std::string>& colors) {
     std::vector<std::array<float, 3>> output(colors.size(), { 0.0f, 0.0f, 0.0f });
     for (size_t i = 0; i < colors.size(); ++i) {
-        const std::string& color = colors[i];
-        const char* c = color.data() + 1;
-        if ((color.size() == 7) && (color.front() == '#')) {
-            for (size_t j = 0; j < 3; ++j) {
-                int digit1 = hex_digit_to_int(*c++);
-                int digit2 = hex_digit_to_int(*c++);
-                if ((digit1 == -1) || (digit2 == -1))
-                    break;
-
-                output[i][j] = float(digit1 * 16 + digit2) * INV_255;
-            }
-        }
+        output[i] = decode_color(colors[i]);
     }
     return output;
 }
@@ -1575,6 +1580,11 @@ void GCodeViewer::render_legend() const
         {
             // extruders
             for (unsigned int i = 0; i < (unsigned int)extruders_count; ++i) {
+                // shows only extruders actually used
+                auto it = std::find(m_extruder_ids.begin(), m_extruder_ids.end(), static_cast<unsigned char>(i));
+                if (it == m_extruder_ids.end())
+                    continue;
+
 #if USE_ICON_HEXAGON
                 add_item(EItemType::Hexagon, m_tool_colors[i], (boost::format(_u8L("Extruder %d")) % (i + 1)).str());
 #else
@@ -1671,14 +1681,9 @@ void GCodeViewer::render_legend() const
     ImGui::PopStyleVar();
 }
 
+
 void GCodeViewer::render_time_estimate() const
 {
-    static const std::vector<std::string> Columns_Headers = {
-        _u8L("Operation"),
-        _u8L("Remaining"),
-        _u8L("Duration")
-    };
-
     if (!m_time_estimate_enabled)
         return;
 
@@ -1686,94 +1691,87 @@ void GCodeViewer::render_time_estimate() const
     if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
         return;
 
-    int columns_count = 1;
-    if (ps.estimated_silent_print_time != "N/A")
-        ++columns_count;
-
     ImGuiWrapper& imgui = *wxGetApp().imgui();
-    Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
-    imgui.set_next_window_pos(static_cast<float>(cnv_size.get_width()), static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f);
-    ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast<float>(cnv_size.get_height())));
-    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
-    ImGui::SetNextWindowBgAlpha(0.6f);
-    imgui.begin(std::string("Time_estimate"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
-
-    ImDrawList* draw_list = ImGui::GetWindowDrawList();
-    float icon_size = ImGui::GetTextLineHeight();
 
     using Time = std::pair<float, float>;
     using TimesList = std::vector<std::pair<CustomGCode::Type, Time>>;
-    using Headers = std::vector<std::string>;
-    using Offsets = std::array<float, 2>;
 
-    auto add_mode = [this, &imgui, icon_size, draw_list](const std::string& mode, const std::string& time, const TimesList& times, const Headers& headers) {
-        auto add_partial_times = [this, &imgui, icon_size, draw_list](const TimesList& times, const Headers& headers) {
-            auto add_color = [this, &imgui, icon_size, draw_list](int id, Offsets& offsets, const Time& time) {
-                ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT);
-                std::string text = _u8L("Color");
-                if (m_view_type != EViewType::ColorPrint)
-                    text += " " + std::to_string(id);
-                imgui.text(text);
-                ImGui::PopStyleColor();
-                ImGui::SameLine();
+    // helper structure containig the data needed to render the items
+    struct Item
+    {
+        CustomGCode::Type type;
+        int extruder_id;
+        Color color;
+        Time time;
+    };
+    using Items = std::vector<Item>;
 
-                if (m_view_type == EViewType::ColorPrint) {
-                    const Color& color = m_tool_colors[id - 1];
-                    ImVec2 pos = ImGui::GetCursorScreenPos();
-#if USE_ICON_HEXAGON
-                    ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size));
-                    draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6);
-#else
-                    draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f },
-                        ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f }));
-#endif // USE_ICON_HEXAGON
-                }
-                ImGui::SameLine(offsets[0]);
-                imgui.text(short_time(get_time_dhms(time.second)));
-                ImGui::SameLine(offsets[1]);
-                imgui.text(short_time(get_time_dhms(time.first)));
+    auto append_mode = [this, &imgui](const std::string& time_str, const Items& items) {
+        auto append_partial_times = [this, &imgui](const Items& items) {
+            using Headers = std::vector<std::string>;
+            const Headers headers = {
+                _u8L("Event"),
+                _u8L("Remaining"),
+                _u8L("Duration")
             };
-            auto calc_offsets = [this, icon_size](const TimesList& times, const Headers& headers, int color_change_count) {
+            using Offsets = std::array<float, 2>;
+            auto calc_offsets = [this, &headers](const Items& items) {
                 Offsets ret = { ImGui::CalcTextSize(headers[0].c_str()).x, ImGui::CalcTextSize(headers[1].c_str()).x };
-                for (const auto& [type, time] : times) {
+                for (const Item& item : items) {
                     std::string label;
-                    switch (type)
+                    switch (item.type)
                     {
-                    case CustomGCode::PausePrint:
-                    {
-                        label = _u8L("Pause");
-                        break;
-                    }
+                    case CustomGCode::PausePrint:  { label = _u8L("Pause"); break; }
                     case CustomGCode::ColorChange:
                     {
-                        label = _u8L("Color");
-                        if (m_view_type != EViewType::ColorPrint)
-                            label += " " + std::to_string(color_change_count);
+                        int extruders_count = wxGetApp().extruders_edited_cnt();
+                        label = (extruders_count > 1) ? _u8L("[XX] Color") : _u8L("Color");
                         break;
                     }
                     default: { break; }
                     }
 
                     ret[0] = std::max(ret[0], ImGui::CalcTextSize(label.c_str()).x);
-                    ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(time.second)).c_str()).x);
+                    ret[1] = std::max(ret[1], ImGui::CalcTextSize(short_time(get_time_dhms(item.time.second)).c_str()).x);
                 }
 
                 const ImGuiStyle& style = ImGui::GetStyle();
-                ret[0] += icon_size + style.ItemSpacing.x;
+                ret[0] += ImGui::GetTextLineHeight() + 2.0f * style.ItemSpacing.x;
                 ret[1] += ret[0] + style.ItemSpacing.x;
                 return ret;
             };
+            auto append_color = [this, &imgui](int id, int extruder_id, const Color& color, Offsets& offsets, const Time& time) {
+                ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT);
+                int extruders_count = wxGetApp().extruders_edited_cnt();
+                std::string text;
+                if (extruders_count > 1)
+                    text = "[" + std::to_string(extruder_id) + "] ";
+                text += _u8L("Color");
+                imgui.text(text);
+                ImGui::PopStyleColor();
+                ImGui::SameLine();
 
-            if (times.empty())
+                float icon_size = ImGui::GetTextLineHeight();
+                ImDrawList* draw_list = ImGui::GetWindowDrawList();
+                ImVec2 pos = ImGui::GetCursorScreenPos();
+                pos.x -= 0.5f * ImGui::GetStyle().ItemSpacing.x;
+#if USE_ICON_HEXAGON
+                ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size));
+                draw_list->AddNgonFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 6);
+#else
+                draw_list->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + icon_size - 1.0f, pos.y + icon_size - 1.0f },
+                    ImGui::GetColorU32({ m_tool_colors[i][0], m_tool_colors[i][1], m_tool_colors[i][2], 1.0f }));
+#endif // USE_ICON_HEXAGON
+                ImGui::SameLine(offsets[0]);
+                imgui.text(short_time(get_time_dhms(time.second)));
+                ImGui::SameLine(offsets[1]);
+                imgui.text(short_time(get_time_dhms(time.first)));
+            };
+
+            if (items.empty())
                 return;
 
-            int color_change_count = 0;
-            for (auto time : times) {
-                if (time.first == CustomGCode::ColorChange)
-                    ++color_change_count;
-            }
-
-            Offsets offsets = calc_offsets(times, headers, color_change_count);
+            Offsets offsets = calc_offsets(items);
 
             ImGui::Spacing();
             ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT);
@@ -1783,13 +1781,11 @@ void GCodeViewer::render_time_estimate() const
             ImGui::SameLine(offsets[1]);
             imgui.text(headers[2]);
             ImGui::PopStyleColor();
+            ImGui::Separator();
 
-            int last_color_id = color_change_count;
-
-            for (int i = static_cast<int>(times.size()) - 1; i >= 0; --i) {
-                const auto& [type, time] = times[i];
-
-                switch (type)
+            unsigned int last_color_id = 0;
+            for (const Item& item : items) {
+                switch (item.type)
                 {
                 case CustomGCode::PausePrint:
                 {
@@ -1797,15 +1793,13 @@ void GCodeViewer::render_time_estimate() const
                     imgui.text(_u8L("Pause"));
                     ImGui::PopStyleColor();
                     ImGui::SameLine(offsets[0]);
-                    imgui.text(short_time(get_time_dhms(time.second - time.first)));
-
-                    add_color(last_color_id, offsets, time);
+                    imgui.text(short_time(get_time_dhms(item.time.second - item.time.first)));
                     break;
                 }
                 case CustomGCode::ColorChange:
                 {
-                    add_color(color_change_count, offsets, time);
-                    last_color_id = color_change_count--;
+                    append_color(last_color_id, item.extruder_id, item.color, offsets, item.time);
+                    ++last_color_id;
                     break;
                 }
                 default: { break; }
@@ -1814,24 +1808,82 @@ void GCodeViewer::render_time_estimate() const
         };
 
         ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_ORANGE_LIGHT);
-        imgui.text(mode + ":");
+        imgui.text(_u8L("Time") + ":");
         ImGui::PopStyleColor();
         ImGui::SameLine();
-        imgui.text(time);
-        add_partial_times(times, headers);
+        imgui.text(time_str);
+        append_partial_times(items);
     };
 
+    auto generate_items = [this](const TimesList& times) {
+        std::vector<Item> items;
+
+        std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
+        int extruders_count = wxGetApp().extruders_edited_cnt();
+        std::vector<Color> last_color(extruders_count);
+        for (int i = 0; i < extruders_count; ++i) {
+            last_color[i] = m_tool_colors[i];
+        }
+        int last_extruder_id = 1;
+        for (const auto& time_rec : times) {
+            switch (time_rec.first)
+            {
+            case CustomGCode::PausePrint:
+            {
+                auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; });
+                if (it != custom_gcode_per_print_z.end()) {
+                    items.push_back({ CustomGCode::ColorChange, it->extruder, last_color[it->extruder - 1], time_rec.second });
+                    items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second });
+                    custom_gcode_per_print_z.erase(it);
+                }
+                break;
+            }
+            case CustomGCode::ColorChange:
+            {
+                auto it = std::find_if(custom_gcode_per_print_z.begin(), custom_gcode_per_print_z.end(), [time_rec](const CustomGCode::Item& item) { return item.type == time_rec.first; });
+                if (it != custom_gcode_per_print_z.end()) {
+                    items.push_back({ time_rec.first, it->extruder, last_color[it->extruder - 1], time_rec.second });
+                    last_color[it->extruder - 1] = decode_color(it->color);
+                    last_extruder_id = it->extruder;
+                    custom_gcode_per_print_z.erase(it);
+                }
+                else
+                    items.push_back({ time_rec.first, last_extruder_id, last_color[last_extruder_id - 1], time_rec.second });
+
+                break;
+            }
+            default: { break; }
+            }
+        }
+
+        return items;
+    };
+
+    Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
+    imgui.set_next_window_pos(static_cast<float>(cnv_size.get_width()), static_cast<float>(cnv_size.get_height()), ImGuiCond_Always, 1.0f, 1.0f);
+    ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(-1.0f, 0.5f * static_cast<float>(cnv_size.get_height())));
+    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
+    ImGui::SetNextWindowBgAlpha(0.6f);
+    imgui.begin(std::string("Time_estimate_2"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
+
     // title
     imgui.title(_u8L("Estimated printing time"));
 
-    // times
-    if (ps.estimated_normal_print_time != "N/A")
-        add_mode(_u8L("Normal mode"), ps.estimated_normal_print_time, ps.estimated_normal_custom_gcode_print_times, Columns_Headers);
-
-    if (ps.estimated_silent_print_time != "N/A") {
-        ImGui::Separator();
-        add_mode(_u8L("Stealth mode"), ps.estimated_silent_print_time, ps.estimated_silent_custom_gcode_print_times, Columns_Headers);
+    // mode tabs
+    ImGui::BeginTabBar("mode_tabs");
+    if (ps.estimated_normal_print_time != "N/A") {
+        if (ImGui::BeginTabItem(_u8L("Normal").c_str())) {
+            append_mode(ps.estimated_normal_print_time, generate_items(ps.estimated_normal_custom_gcode_print_times));
+            ImGui::EndTabItem();
+        }
     }
+    if (ps.estimated_silent_print_time != "N/A") {
+        if (ImGui::BeginTabItem(_u8L("Stealth").c_str())) {
+            append_mode(ps.estimated_silent_print_time, generate_items(ps.estimated_silent_custom_gcode_print_times));
+            ImGui::EndTabItem();
+        }
+    }
+    ImGui::EndTabBar();
 
     imgui.end();
     ImGui::PopStyleVar();
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 2c463dc2a..1253c047e 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -1028,6 +1028,13 @@ void ImGuiWrapper::init_style()
 
     // Separator
     set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT);
+
+    // Tabs
+    set_color(ImGuiCol_Tab, COL_ORANGE_DARK);
+    set_color(ImGuiCol_TabHovered, COL_ORANGE_LIGHT);
+    set_color(ImGuiCol_TabActive, COL_ORANGE_LIGHT);
+    set_color(ImGuiCol_TabUnfocused, COL_GREY_DARK);
+    set_color(ImGuiCol_TabUnfocusedActive, COL_GREY_LIGHT);
 }
 
 void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)