From c112feb8582094e7e473b53079bbc95f99a497f1 Mon Sep 17 00:00:00 2001
From: Filip Sykala <filip.sykala@prusa3d.cz>
Date: Wed, 6 Oct 2021 16:36:50 +0200
Subject: [PATCH] Add icon instead of text for rename and delete font item

---
 src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 250 +++++++++++++++---------
 src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp |  13 +-
 2 files changed, 167 insertions(+), 96 deletions(-)

diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
index 8192f5410..d6a035192 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
@@ -23,6 +23,8 @@
 #include <wx/fontutil.h>
 #include <wx/fontdlg.h>
 
+#include <GL/glew.h>
+
 #ifdef __APPLE__
 #include <wx/uri.h>
 #include <CoreText/CTFont.h>
@@ -154,6 +156,9 @@ void GLGizmoEmboss::initialize()
     if (m_is_initialized) return;
     m_is_initialized = true;
 
+    // TODO: What to do when icon was NOT loaded?
+    bool success = init_icons();
+
     load_font_list();    
 
     m_gui_cfg.emplace(GuiCfg());
@@ -169,11 +174,9 @@ void GLGizmoEmboss::initialize()
         m_font_selected = 0; // select first    
         is_font_loaded  = load_font();
     }
-    //sort_fonts();
     set_default_configuration();
 }
 
-
 void GLGizmoEmboss::load_font_list() 
 {
     AppConfig *cfg = wxGetApp().app_config;
@@ -265,11 +268,13 @@ ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection,
 // create_text_volume()
 bool GLGizmoEmboss::process() 
 {
+    // exist loaded font?
     if (!m_font.has_value()) return false;
-
     ExPolygons shapes = Emboss::text2shapes(*m_font, m_text.c_str(), m_font_prop);
-    if (shapes.empty()) return false; 
-        
+    // exist 2d shape made by text ?
+    // (no shape means that font doesnt have any of text symbols)
+    if (shapes.empty()) return false;
+
     float scale = m_font_prop.size_in_mm / m_font->ascent;
     auto project = std::make_unique<Emboss::ProjectScale>(
         std::make_unique<Emboss::ProjectZ>(m_font_prop.emboss / scale), scale);
@@ -363,9 +368,10 @@ void GLGizmoEmboss::draw_window()
     if (!m_font.has_value()) {
         ImGui::Text(_L("Warning: No font is selected. Select correct one.").c_str());
     }
+    float left, right, bottom, top;
 
     draw_font_list();
-    
+
     //ImGui::SameLine();
     //if (ImGui::Button(_L("use system font").c_str())) {
     //    wxSystemSettings ss;
@@ -409,6 +415,8 @@ void GLGizmoEmboss::draw_font_list()
     auto &current = m_font_list[m_font_selected];
     std::optional<int> rename_index;
     if (ImGui::BeginCombo("##font_selector", current.name.c_str())) {
+        float combo_width = ImGui::GetItemRectSize().x 
+            - (ImGui::GetTextLineHeightWithSpacing() - ImGui::GetTextLineHeight());
         // first line
 #ifdef USE_FONT_DIALOG
         if (ImGui::Button(_L("Choose font").c_str())) {
@@ -433,31 +441,7 @@ void GLGizmoEmboss::draw_font_list()
                     f.name :
                     (f.name.substr(0, m_gui_cfg->max_font_name - 3) + " ..");
             int index = &f - &m_font_list.front();
-
             bool is_selected = index == static_cast<int>(m_font_selected);
-            // must be befor selectable to be focusable            
-            // add delete button
-            
-            if (!is_selected) {
-                auto pos_x = ImGui::GetCursorPosX();
-                // add rename button
-                ImGui::SetCursorPosX(140);
-                if (ImGui::Button("rename")) rename_index = index;
-
-                ImGui::SameLine();
-                ImGui::SetCursorPosX(200);
-                m_imgui->disabled_begin(is_selected);
-                if (ImGui::Button("del")) {
-                    m_font_list.erase(m_font_list.begin() + index);
-                    // fix selected index
-                    if (index < m_font_selected) --m_font_selected;
-                    store_font_list();
-                }
-                m_imgui->disabled_end(); // exist_rename || is_selected
-                ImGui::SameLine();
-                ImGui::SetCursorPosX(pos_x);
-            }
-
             if (ImGui::Selectable(name.c_str(), is_selected)) {
                 size_t prev_font_selected = m_font_selected;
                 m_font_selected           = index;
@@ -467,30 +451,27 @@ void GLGizmoEmboss::draw_font_list()
                     process();
                 }
             }
+
+            ImGui::SameLine();
+            auto pos = ImGui::GetCursorPos();
+            // add rename button
+            ImGui::SetCursorPosX(combo_width - 2*ImGui::GetTextLineHeightWithSpacing());
+            if (draw_button(IconType::rename, false)) rename_index = index;
+
+            ImGui::SameLine();
+            ImGui::SetCursorPosX(combo_width - ImGui::GetTextLineHeightWithSpacing());
+            if (draw_button(IconType::erase, is_selected)) {
+                m_font_list.erase(m_font_list.begin() + index);
+                // fix selected index
+                if (index < m_font_selected) --m_font_selected;
+                store_font_list();
+            }
+            //ImGui::SetCursorPosX(pos_x);
             ImGui::PopID();
         }
         ImGui::EndCombo();
     }
 
-    // when more collection add selector
-    if (m_font.has_value() && m_font->count > 1) {
-        ImGui::SameLine();
-        if (ImGui::BeginCombo("##font_collection_selector",
-                              std::to_string(m_font->index).c_str())) {
-            for (unsigned int i = 0; i < m_font->count; ++i) {
-                ImGui::PushID(1 << 10 + i);
-                if (ImGui::Selectable(std::to_string(i).c_str(),
-                                      i == m_font->index)) {
-                    m_font->index = i;
-                    m_font->cache.clear();
-                    process();
-                }
-                ImGui::PopID();
-            }
-            ImGui::EndCombo();
-        }
-    }
-
     // rename modal window popup
     const char *rename_popup_id = "Rename modal window";
     static int rename_id;
@@ -511,20 +492,38 @@ void GLGizmoEmboss::draw_font_list()
 }
 
 void GLGizmoEmboss::draw_advanced() {
-    if (ImGui::InputFloat("Size[in mm]", &m_font_prop.size_in_mm)) {
+    if (ImGui::InputFloat(_L("Size[in mm]").c_str(), &m_font_prop.size_in_mm)) {
         if (m_font_prop.size_in_mm < 0.1) m_font_prop.size_in_mm = 10;
         process();
     }
-    if (ImGui::InputFloat("Emboss[in mm]", &m_font_prop.emboss)) process();
-    if (ImGui::InputFloat("Flatness", &m_font_prop.flatness)) {
+    if (ImGui::InputFloat(_L("Emboss[in mm]").c_str(), &m_font_prop.emboss)) process();
+    if (ImGui::InputFloat(_L("Flatness").c_str(), &m_font_prop.flatness)) {
         if (m_font.has_value()) m_font->cache.clear();
         process();
     }
-    if (ImGui::InputInt("CharGap[in font points]", &m_font_prop.char_gap))
+    if (ImGui::InputInt(_L("CharGap[in font points]").c_str(), &m_font_prop.char_gap))
         process();
-    if (ImGui::InputInt("LineGap[in font points]", &m_font_prop.line_gap))
+    if (ImGui::InputInt(_L("LineGap[in font points]").c_str(), &m_font_prop.line_gap))
         process();
 
+    // when more collection add selector
+    if (m_font.has_value() && m_font->count > 1) {
+        if (ImGui::BeginCombo(_L("Font collection").c_str(),
+                              std::to_string(m_font->index).c_str())) {
+            for (unsigned int i = 0; i < m_font->count; ++i) {
+                ImGui::PushID(1 << 10 + i);
+                if (ImGui::Selectable(std::to_string(i).c_str(),
+                                      i == m_font->index)) {
+                    m_font->index = i;
+                    m_font->cache.clear();
+                    process();
+                }
+                ImGui::PopID();
+            }
+            ImGui::EndCombo();
+        }
+    }
+
     // ImGui::InputFloat3("Origin", m_orientation.origin.data());
     // if (ImGui::InputFloat3("Normal", m_normal.data())) m_normal.normalize();
     // if (ImGui::InputFloat3("Up", m_up.data())) m_up.normalize();
@@ -589,8 +588,7 @@ bool GLGizmoEmboss::choose_font_by_wxdialog()
         MessageDialog not_loaded_font_message(nullptr,  message, title, wxOK);
         not_loaded_font_message.ShowModal();
         return choose_font_by_wxdialog();
-    }
-    //sort_fonts();    
+    }  
     return true;
 }
 
@@ -604,20 +602,24 @@ bool GLGizmoEmboss::choose_true_type_file()
                         wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
     if (dialog.ShowModal() == wxID_OK) dialog.GetPaths(input_files);
     if (input_files.IsEmpty()) return false;
-
-    FontList font_list;
-    font_list.reserve(input_files.size());
+    bool font_loaded = false;
     for (auto &input_file : input_files) {
         std::string path = std::string(input_file.c_str());
         size_t      pos  = path.find_last_of('\\');
         size_t      pos2 = path.find_last_of('.');
         std::string name = path.substr(pos + 1, pos2 - pos - 1);
-        font_list.emplace_back(name, path);
+        m_font_list.emplace_back(name, path);
+
+        // set first valid added font as active
+        if (!font_loaded) {
+            if (!load_font(m_font_list.size() - 1))
+                m_font_list.pop_back();
+            else
+                font_loaded = true;
+        }
     }
-    // set last added font as active
-    m_font_selected = m_font_list.size() + font_list.size() - 1;
-    add_fonts(font_list);
-    return load_font();
+    if (font_loaded) process();
+    return font_loaded;
 }
 
 bool GLGizmoEmboss::choose_svg_file() 
@@ -653,32 +655,6 @@ bool GLGizmoEmboss::choose_svg_file()
     return add_volume(name, its);
 }
 
-void GLGizmoEmboss::sort_fonts() {
-    // initialize original index locations
-    std::vector<size_t> idx(m_font_list.size());
-    std::iota(idx.begin(), idx.end(), 0);
-
-    std::stable_sort(idx.begin(), idx.end(),
-        [this](size_t i1, size_t i2) {
-            return m_font_list[i1].name < m_font_list[i2].name;
-        });
-    
-    FontList font_list;    
-    font_list.reserve(m_font_list.size());
-    size_t selected = 0;
-    for (const size_t &i : idx) { 
-        if (i == m_font_selected) selected = &i - &idx.front();
-        font_list.emplace_back(m_font_list[i]); 
-    }
-    m_font_list = font_list;
-    m_font_selected = selected;    
-}
-
-void GLGizmoEmboss::add_fonts(const FontList &font_list) {
-    m_font_list.insert(m_font_list.end(), font_list.begin(), font_list.end());
-    sort_fonts();
-}
-
 TextConfiguration GLGizmoEmboss::create_configuration() {
     return TextConfiguration(m_font_list[m_font_selected], m_font_prop, m_text);
 }
@@ -699,14 +675,15 @@ bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
     // when not in font list add to list
     if (index >= m_font_list.size()) {
         m_font_selected = m_font_list.size();
-        add_fonts({configuration.font_item});
+        // TODO: what to do with new font item? 
+        m_font_list.emplace_back(configuration.font_item);
     } else {
         m_font_selected = index;
     }
     // When can't load font
     if (!load_font()) {
         // remove bad loadabled font, for correct prev index
-        m_font_list.erase(m_font_list.begin() + m_font_selected);
+        m_font_list.pop_back();
         m_font_selected = prev_font_selected;
         return false;
     }
@@ -726,6 +703,95 @@ std::string GLGizmoEmboss::create_volume_name()
         (m_text.substr(0, max_len - 3) + " ..") : m_text);
 }
 
+bool GLGizmoEmboss::init_icons()
+{
+    std::string path = resources_dir() + "/icons/white/";
+
+    // icon order has to match the enum IconType
+    std::vector<std::string> filenames = {
+        path +"wrench.svg", 
+        path +"delete.svg"
+    };
+
+    // state order has to match the enum IconState
+    std::vector<std::pair<int, bool>> states;
+    states.push_back(std::make_pair(1, false)); // Activable
+    states.push_back(std::make_pair(0, true)); // Hovered
+    states.push_back(std::make_pair(2, false)); // Disabled
+
+    unsigned int sprite_size_px = std::ceil(ImGui::GetTextLineHeight());
+    // make size pair number
+    if (sprite_size_px % 2 != 0) ++sprite_size_px;
+    bool compress = false;
+    return m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, compress);
+}
+
+void GLGizmoEmboss::draw_icon(IconType icon, IconState state)
+{
+    unsigned int icons_texture_id = m_icons_texture.get_id();
+    int          tex_width        = m_icons_texture.get_width();
+    int          tex_height       = m_icons_texture.get_height();
+
+    // is icon loaded
+    if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1))
+        return;
+    ImTextureID tex_id = (void *) (intptr_t) (GLuint) icons_texture_id;
+    // ImVec2 image_size(tex_width, tex_height);
+
+    size_t count_icons  = 2; // wrench | delete
+    size_t count_states = 3; // activable | hovered | disabled
+    ImVec2 icon_size(tex_width / count_states, tex_height / count_icons);
+
+    ImVec2 start(
+        static_cast<unsigned>(state)*icon_size.x,
+        static_cast<unsigned>(icon)*icon_size.y);
+
+    ImVec2 uv0(
+        start.x/tex_width, 
+        start.y/tex_height);
+
+    ImVec2 uv1(
+        (start.x + icon_size.x) / tex_width, 
+        (start.y + icon_size.y) / tex_height);
+
+    ImGui::Image(tex_id, icon_size, uv0, uv1);
+}
+
+bool GLGizmoEmboss::draw_button(IconType icon, bool disable) 
+{
+    float line_spacing = ImGui::GetTextLineHeightWithSpacing() -
+                         ImGui::GetTextLineHeight();
+    float cursor_pos_y = ImGui::GetCursorPosY();
+    ImGui::SetCursorPosY(cursor_pos_y - line_spacing/2);
+    ScopeGuard sg([cursor_pos_y]() { ImGui::SetCursorPosY(cursor_pos_y); ImGui::NewLine();});
+
+    if (disable) {
+        draw_icon(icon, IconState::disabled);
+        if (ImGui::IsItemHovered() && icon == IconType::erase) 
+            ImGui::SetTooltip(_L("Active font can't be removed").c_str());
+        return false;
+    }
+
+    float cursor_x = ImGui::GetCursorPosX();
+    
+    draw_icon(icon, IconState::activable);
+    if (ImGui::IsItemClicked()) return true;
+    if (ImGui::IsItemHovered()) {
+        switch (icon) {
+        case IconType::rename: ImGui::SetTooltip(_L("rename").c_str()); break;
+        case IconType::erase: ImGui::SetTooltip(_L("delete").c_str()); break;
+        default: break;
+        }        
+        // redraw image over previous
+        ImGui::SameLine();
+        ImGui::SetCursorPosX(cursor_x);
+
+        draw_icon(icon, IconState::hovered);
+        if (ImGui::IsItemClicked()) return true;
+    }
+    return false;
+}
+
 std::optional<Emboss::Font> WxFontUtils::load_font(const FontItem &fi)
 {
     switch (fi.type) {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
index ef12b90fc..b4000efb8 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
@@ -4,6 +4,7 @@
 // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
 // which overrides our localization "L" macro.
 #include "GLGizmoBase.hpp"
+#include "slic3r/GUI/GLTexture.hpp"
 
 #include "admesh/stl.h" // indexed_triangle_set
 #include <optional>
@@ -14,7 +15,6 @@
 #include "libslic3r/Model.hpp"
 
 namespace Slic3r::GUI {
-
 class GLGizmoEmboss : public GLGizmoBase
 {    
 public:
@@ -58,9 +58,6 @@ private:
     bool choose_true_type_file();
     bool choose_svg_file();
 
-    void sort_fonts();
-    void add_fonts(const FontList &font_list);
-
     // Create object described how to make a Volume
     TextConfiguration create_configuration();
     bool load_configuration(ModelVolume *volume);
@@ -105,6 +102,14 @@ private:
     // initialize when GL is accessible
     bool m_is_initialized;
 
+    // drawing icons
+    GLTexture m_icons_texture;
+    bool init_icons();
+    enum class IconType: unsigned { rename = 0, erase /*1*/};
+    enum class IconState: unsigned { activable = 0, hovered /*1*/, disabled /*2*/};
+    void draw_icon(IconType icon, IconState state);
+    bool draw_button(IconType icon, bool disable);
+
     static const std::string M_APP_CFG_FONT_LIST;
     // only temporary solution
     static const std::string M_ICON_FILENAME;