diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 970b349cb..e362cf74d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3442,6 +3442,41 @@ bool GLCanvas3D::_is_shown_on_screen() const return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } +// Getter for the const char*[] +static bool string_getter(const bool is_undo, int idx, const char** out_text) +{ + return wxGetApp().plater()->undo_redo_string_getter(is_undo, idx, out_text); +} + +void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) +{ + if (m_canvas != nullptr && m_toolbar.get_imgui_visible(is_undo)) + { + const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo")); + ImGuiWrapper* imgui = wxGetApp().imgui(); + + const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); + imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always); + + imgui->set_next_window_bg_alpha(0.5f); + imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + int hovered = m_toolbar.get_imgui_hovered_pos(); + int selected = -1; + const float em = static_cast(wxGetApp().em_unit()); + + if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) + m_toolbar.set_imgui_hovered_pos(hovered); + if (selected >= 0) + m_toolbar.hide_imgui(is_undo); + + imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); + + imgui->end(); + } +} + bool GLCanvas3D::_init_toolbar() { if (!m_toolbar.is_enabled()) @@ -3627,37 +3662,19 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.action_callback = [this]() - { + item.is_toggable = false; + item.action_callback = [this]() { if (m_canvas != nullptr) { wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); - m_toolbar.set_imgui_visible(); + m_toolbar.activate_imgui(true); } }; item.visibility_callback = []()->bool { return true; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_undo(); }; - item.render_callback = [this]() - { - if (m_canvas != nullptr && m_toolbar.get_imgui_visible()) { - ImGuiWrapper* imgui = wxGetApp().imgui(); - - const float approx_height = m_toolbar.get_height(); - imgui->set_next_window_pos(600, approx_height, ImGuiCond_Always); - - imgui->set_next_window_bg_alpha(0.5f); - imgui->begin(_(L("Undo Stack")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - std::vector undo_stack = {"A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D",}; - int sel = 4; - imgui->multi_sel_list("", undo_stack, sel); - - const bool undo_clicked = imgui->button(_(L("Undo N Action"))); - - imgui->end(); - if (undo_clicked) - m_toolbar.set_imgui_visible(false); - } + item.enabled_state_callback = [this]()->bool { + if (!wxGetApp().plater()->can_undo()) { m_toolbar.hide_imgui(true); return false; } + return true; }; + item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(true, pos_x); }; if (!m_toolbar.add_item(item)) return false; @@ -3667,15 +3684,20 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_redo(); }; - item.render_callback = []() {}; + item.action_callback = [this]() { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); + m_toolbar.activate_imgui(false); + } + }; + item.enabled_state_callback = [this]()->bool { + if (!wxGetApp().plater()->can_redo()) { m_toolbar.hide_imgui(false); return false; } + return true; + }; + item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(false, pos_x); }; if (!m_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) - return false; - return true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 47b1c5ec2..4867f94ce 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -676,6 +676,7 @@ private: #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices() const; void _render_selection_sidebar_hints() const; + void _render_undo_redo_stack(const bool is_undo, float pos_x); void _update_volumes_hover_state() const; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index a851e3a4c..f6140464b 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -35,7 +35,7 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; }; -const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](){}; +const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){}; GLToolbarItem::Data::Data() : name("") @@ -84,7 +84,7 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); - m_data.render_callback(); + m_data.render_callback(left, right, bottom, top); } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index aa68fae38..e32e4a41e 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -37,7 +37,7 @@ public: typedef std::function ActionCallback; typedef std::function VisibilityCallback; typedef std::function EnabledStateCallback; - typedef std::function RenderCallback; + typedef std::function RenderCallback; enum EType : unsigned char { @@ -252,7 +252,10 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - bool m_imgui_visible {false}; + bool m_undo_imgui_visible {false}; + bool m_redo_imgui_visible {false}; + int m_imgui_hovered_pos { -1 }; + int m_imgui_selected_pos { -1 }; public: #if ENABLE_SVG_ICONS @@ -309,8 +312,21 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - void set_imgui_visible(bool visible = true) { m_imgui_visible = visible; } - bool get_imgui_visible() { return m_imgui_visible; } + // undo == true => "undo" imgui is activated + // undo == false => "redo" imgui is activated + bool get_imgui_visible(const bool undo) const { return undo ? m_undo_imgui_visible : m_redo_imgui_visible; } + void hide_imgui(const bool undo) { undo ? m_undo_imgui_visible = false : m_redo_imgui_visible = false; } + void activate_imgui(const bool undo) { + m_undo_imgui_visible = undo; + m_redo_imgui_visible = !undo; + m_imgui_hovered_pos = m_imgui_selected_pos = -1; + } + + void set_imgui_hovered_pos(int pos = -1) { m_imgui_hovered_pos = pos; } + int get_imgui_hovered_pos() const { return m_imgui_hovered_pos; } + + void set_imgui_selected_pos(int pos = -1) { m_imgui_selected_pos = pos; } + int get_imgui_selected_pos() const { return m_imgui_selected_pos; } private: void calc_layout() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index cbcf33f77..7f4a6c10c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -342,25 +342,30 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& return res; } -// Getter for the const char*[] -static bool StringGetter(void* data, int i, const char** out_text) +bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected) { - const std::vector* v = (std::vector*)data; - if (out_text) - *out_text = (*v)[i].c_str(); - return true; -} + bool is_hovered = false; + ImGui::ListBoxHeader("", size); -bool ImGuiWrapper::multi_sel_list(const wxString& label, const std::vector& options, int& selection) -{ - // this is to force the label to the left of the widget: - if (!label.IsEmpty()) - text(label); + int i=0; + const char* item_text; + while (items_getter(is_undo, i, &item_text)) + { + ImGui::Selectable(item_text, i < hovered); - bool res = false; - ImGui::ListBox("", &selection, StringGetter, (void*)&options, (int)options.size()); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(item_text); + hovered = i; + is_hovered = true; + } - return res; + if (ImGui::IsItemClicked()) + selected = i; + i++; + } + + ImGui::ListBoxFooter(); + return is_hovered; } void ImGuiWrapper::disabled_begin(bool disabled) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 42c562b72..a18b15184 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -67,7 +67,7 @@ public: void text(const std::string &label); void text(const wxString &label); bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected - bool multi_sel_list(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected + bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected); void disabled_begin(bool disabled); void disabled_end(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6ac58eb84..3dfc1d1b8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4110,6 +4110,21 @@ void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot( void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } void Plater::undo() { p->undo(); } void Plater::redo() { p->redo(); } +bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text) +{ + const size_t& active_snapshot_time = p->undo_redo_stack.active_snapshot_time(); + const std::vector& ss_stack = p->undo_redo_stack.snapshots(); + const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time)); + + const int idx_in_ss_stack = it - ss_stack.begin() + (is_undo ? -(++idx) : idx); + + if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { + *out_text = ss_stack[idx_in_ss_stack].name.c_str(); + return true; + } + + return false; +} void Plater::on_extruders_change(int num_extruders) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 0be465f53..7f379c638 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -187,6 +187,7 @@ public: void take_snapshot(const wxString &snapshot_name); void undo(); void redo(); + bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text); void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config);