From a5322ef970bce5489f242aa5c86c2c997c0d4984 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 7 Mar 2023 09:37:48 +0100 Subject: [PATCH 01/13] DoubleSlider: ImGui rendering (WIP) --- src/slic3r/GUI/DoubleSlider.cpp | 31 +++++++++++++++++++++++++++++++ src/slic3r/GUI/DoubleSlider.hpp | 5 ++++- src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ src/slic3r/GUI/GUI_Preview.cpp | 7 +++++++ src/slic3r/GUI/GUI_Preview.hpp | 2 ++ src/slic3r/GUI/Plater.cpp | 12 ++++++++++++ src/slic3r/GUI/Plater.hpp | 2 ++ 7 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 96313e19b1..623601fd12 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,8 @@ #include "format.hpp" #include "NotificationManager.hpp" +#include "ImGuiPureWrap.hpp" + namespace Slic3r { using GUI::from_u8; @@ -560,6 +563,34 @@ void Control::render() } } +void Control::imgui_render(GUI::GLCanvas3D& canvas) +{ + GUI::Size cnv_size = canvas.get_canvas_size(); + ImVec2 mouse_pos = ImGui::GetMousePos(); + const float width = get_min_size().x*5; + const float height = float(cnv_size.get_height()); + + ImVec2 win_pos(1.0f * (float)cnv_size.get_width(), 1.0f); + ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); + ImGuiPureWrap::set_next_window_size(width, height - 2.f, ImGuiCond_Always); + + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); + + // name of window indentifies window - has to be unique string + std::string name = "DblSlider"; + + int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse | + ImGuiWindowFlags_NoFocusOnAppearing; + + if (ImGuiPureWrap::begin(name, flags)) { + + } + ImGuiPureWrap::end(); +} + bool Control::is_wipe_tower_layer(int tick) const { if (!m_is_wipe_tower || tick >= (int)m_values.size()) diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index f7b370f730..1e20a4b6e8 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -7,6 +7,7 @@ #include "libslic3r/CustomGCode.hpp" #include "wxExtensions.hpp" +#include "GLCanvas3D.hpp" #include #include @@ -302,7 +303,7 @@ public: void show_cog_icon_context_menu(); void auto_color_change(); - ExtrudersSequence m_extruders_sequence; + void imgui_render(GUI::GLCanvas3D& canvas); protected: @@ -431,6 +432,8 @@ private: std::vector m_alternate_values; + ExtrudersSequence m_extruders_sequence; + // control's view variables wxCoord SLIDER_MARGIN; // margin around slider diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 42e743bc88..f00660708d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1950,6 +1950,8 @@ void GLCanvas3D::render() wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); + wxGetApp().plater()->render_imgui_double_slider(*this); + wxGetApp().imgui()->render(); m_canvas->SwapBuffers(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 3765bb2cc3..ed20c4cb64 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -347,6 +347,13 @@ void Preview::sys_color_changed() m_layers_slider->sys_color_changed(); } + +void Preview::render_imgui_double_slider(GLCanvas3D& canvas) +{ + if (m_layers_slider && m_layers_slider->IsShown()) + m_layers_slider->imgui_render(canvas); +} + void Preview::jump_layers_slider(wxKeyEvent& evt) { if (m_layers_slider) m_layers_slider->OnChar(evt); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 6141318845..c720cf771d 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -140,6 +140,8 @@ public: void move_layers_slider(wxKeyEvent& evt); void edit_layers_slider(wxKeyEvent& evt); + void render_imgui_double_slider(GLCanvas3D& canvas); + bool is_loaded() const { return m_loaded; } void update_moves_slider(std::optional visible_range_min = std::nullopt, std::optional visible_range_max = std::nullopt); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f3b289f519..1db3fac3f4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -385,6 +385,7 @@ struct Plater::priv void set_current_canvas_as_dirty(); GLCanvas3D* get_current_canvas3D(); + void render_imgui_double_slider(GLCanvas3D& canvas); void unbind_canvas_event_handlers(); void reset_canvas_volumes(); @@ -3189,6 +3190,12 @@ GLCanvas3D* Plater::priv::get_current_canvas3D() return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); } +void Plater::priv::render_imgui_double_slider(GLCanvas3D& canvas) +{ + if (current_panel == preview) + preview->render_imgui_double_slider(canvas); +} + void Plater::priv::unbind_canvas_event_handlers() { if (view3D != nullptr) @@ -6402,6 +6409,11 @@ GLCanvas3D* Plater::get_current_canvas3D() return p->get_current_canvas3D(); } +void Plater::render_imgui_double_slider(GLCanvas3D& canvas) +{ + p->render_imgui_double_slider(canvas); +} + static std::string concat_strings(const std::set &strings, const std::string &delim = "\n") { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index eec067d612..97c6c18772 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -271,6 +271,8 @@ public: GLCanvas3D* canvas3D(); const GLCanvas3D * canvas3D() const; GLCanvas3D* get_current_canvas3D(); + + void render_imgui_double_slider(GLCanvas3D& canvas); void arrange(); void arrange(Worker &w, bool selected); From a26d17d689ea83e0245dc38d7d92080f770455ef Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 7 Mar 2024 17:28:33 +0100 Subject: [PATCH 02/13] ImguiDoubleSliser: WIP: Some of code is ported from BambuStudio (state of 96707fc4b4b40c30b7e5610d2489ef283fe952a4) * Code improvements : + added render_button() + added draw_hexagon() --- src/imgui/imconfig.h | 2 + src/slic3r/GUI/DoubleSlider.cpp | 736 +++++++++++++++++++++++++++++-- src/slic3r/GUI/DoubleSlider.hpp | 21 +- src/slic3r/GUI/GUI_Preview.cpp | 2 + src/slic3r/GUI/ImGuiPureWrap.cpp | 14 + src/slic3r/GUI/ImGuiPureWrap.hpp | 2 + src/slic3r/GUI/ImGuiWrapper.cpp | 302 +++++++++++++ src/slic3r/GUI/ImGuiWrapper.hpp | 6 + 8 files changed, 1056 insertions(+), 29 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 8045936c4c..ba3cd1c368 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -153,6 +153,8 @@ namespace ImGui const wchar_t PlugMarker = 0x1C; const wchar_t DowelMarker = 0x1D; const wchar_t SnapMarker = 0x1E; + const wchar_t Lock = 0x1F; + const wchar_t Unlock = 0x17; const wchar_t HorizontalHide = 0xB1; const wchar_t HorizontalShow = 0xB2; // Do not forget use following letters only in wstring diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 623601fd12..db4d840327 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -37,17 +37,39 @@ #include "ImGuiPureWrap.hpp" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include + namespace Slic3r { using GUI::from_u8; using GUI::into_u8; using GUI::format_wxstr; +using GUI::format; + +using namespace GUI; namespace DoubleSlider { constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 +static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar +static const float SLIDER_LENGTH = 680.0f; +static const float TEXT_WIDTH_DUMMY = 63.0f; +static const ImVec2 ONE_LAYER_OFFSET = ImVec2(41.0f, /*44*/33.0f); +static const ImVec2 HORIZONTAL_SLIDER_SIZE = ImVec2(764.0f, 90.0f);//764 = 680 + handle_dummy_width * 2 + text_right_dummy +static const ImVec2 VERTICAL_SLIDER_SIZE = ImVec2(105.0f, 748.0f);//748 = 680 + text_dummy_height * 2 + +const ImU32 tooltip_bg = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT); +const ImU32 handle_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT); +const ImU32 handle_border_clr = IM_COL32(255, 255, 255, 255);//white_bg; + +int m_tick_value = -1; +ImVec4 m_tick_rect; + bool equivalent_areas(const double& bottom_area, const double& top_area) { return fabs(bottom_area - top_area) <= miscalculation; @@ -563,32 +585,690 @@ void Control::render() } } -void Control::imgui_render(GUI::GLCanvas3D& canvas) -{ - GUI::Size cnv_size = canvas.get_canvas_size(); - ImVec2 mouse_pos = ImGui::GetMousePos(); - const float width = get_min_size().x*5; - const float height = float(cnv_size.get_height()); - - ImVec2 win_pos(1.0f * (float)cnv_size.get_width(), 1.0f); - ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); - ImGuiPureWrap::set_next_window_size(width, height - 2.f, ImGuiCond_Always); - - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg)); - - // name of window indentifies window - has to be unique string - std::string name = "DblSlider"; - - int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoScrollbar | - ImGuiWindowFlags_NoScrollWithMouse | - ImGuiWindowFlags_NoFocusOnAppearing; - - if (ImGuiPureWrap::begin(name, flags)) { +// ImGuiDS +float Control::get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect) { + float pos_ratio = (v_max - v_min) != 0 ? ((float)(value - v_min) / (float)(v_max - v_min)) : 0.0f; + float handle_pos; + if (is_horizontal()) { + handle_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio; } - ImGuiPureWrap::end(); + else { + pos_ratio = 1.0f - pos_ratio; + handle_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio; + } + return handle_pos; +} + +void Control::draw_background(const ImRect& groove) +{ + const ImU32 bg_rect_col = IM_COL32(255, 255, 255, 255); + const ImU32 groove_col = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); + + if (is_horizontal() || m_ticks.empty()) { + ImVec2 groove_padding = ImVec2(2.0f, 2.0f) * m_scale; + + ImRect bg_rect = groove; + bg_rect.Expand(groove_padding); + + // draw bg of slider + ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, bg_rect_col, false, 0.5 * bg_rect.GetWidth()); + // draw bg of scroll + ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth()); + } + else { + ImVec2 groove_padding = ImVec2(3.0f, 4.0f) * m_scale; + + ImRect bg_rect = groove; + bg_rect.Expand(groove_padding); + + // draw bg of slider + ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, bg_rect_col, false, bg_rect.GetWidth() * 0.5); + // draw bg of scroll + ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth()); + } +} + +void Control::draw_ticks(const ImRect& slideable_region) { + //if(m_draw_mode != dmRegular) + // return; + //if (m_ticks.empty() || m_mode == MultiExtruder) + // return; + if (m_ticks.empty()) + return; + + ImGuiContext& context = *GImGui; + + ImVec2 tick_box = ImVec2(46.0f, 16.0f) * m_scale; + ImVec2 tick_offset = ImVec2(19.0f, 11.0f) * m_scale; + float tick_width = 1.0f * m_scale; + ImVec2 icon_offset = ImVec2(13.0f, 7.0f) * m_scale; + ImVec2 icon_size = ImVec2(14.0f, 14.0f) * m_scale; + + const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); //IM_COL32(144, 144, 144, 255); + const ImU32 tick_hover_box_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); //IM_COL32(219, 253, 231, 255); + const ImU32 delete_btn_clr = IM_COL32(144, 144, 144, 255); + + auto get_tick_pos = [this, slideable_region](int tick) + { + int v_min = GetMinValue(); + int v_max = GetMaxValue(); + return get_pos_from_value(v_min, v_max, tick, slideable_region); + }; + + std::set::const_iterator tick_it = m_ticks.ticks.begin(); + while (tick_it != m_ticks.ticks.end()) + { + float tick_pos = get_tick_pos(tick_it->tick); + + //draw tick hover box when hovered + ImRect tick_hover_box = ImRect(slideable_region.GetCenter().x - tick_box.x / 2, tick_pos - tick_box.y / 2, slideable_region.GetCenter().x + tick_box.x / 2, + tick_pos + tick_box.y / 2); + + if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max)) + { + ImGui::RenderFrame(tick_hover_box.Min, tick_hover_box.Max, tick_hover_box_clr, false); + if (context.IO.MouseClicked[0]) { + m_tick_value = tick_it->tick; + m_tick_rect = ImVec4(tick_hover_box.Min.x, tick_hover_box.Min.y, tick_hover_box.Max.x, tick_hover_box.Max.y); + } + } + ++tick_it; + } + + tick_it = m_ticks.ticks.begin(); + while (tick_it != m_ticks.ticks.end()) + { + float tick_pos = get_tick_pos(tick_it->tick); + + //draw ticks + ImRect tick_left = ImRect(slideable_region.GetCenter().x - tick_offset.x, tick_pos - tick_width, slideable_region.GetCenter().x - tick_offset.y, tick_pos); + ImRect tick_right = ImRect(slideable_region.GetCenter().x + tick_offset.y, tick_pos - tick_width, slideable_region.GetCenter().x + tick_offset.x, tick_pos); + ImGui::RenderFrame(tick_left.Min, tick_left.Max, tick_clr, false); + ImGui::RenderFrame(tick_right.Min, tick_right.Max, tick_clr, false); + + //draw pause icon + if (tick_it->type == PausePrint) { + //ImTextureID pause_icon_id = m_pause_icon_id; + ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, tick_pos - icon_offset.y); + //button_with_pos(pause_icon_id, icon_size, icon_pos); + if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) { + if (context.IO.MouseClicked[0]) + int a = 0; + } + } + ++tick_it; + } + + tick_it = m_selection == ssHigher ? m_ticks.ticks.find(TickCode{ this->GetHigherValue() }) : + m_selection == ssLower ? m_ticks.ticks.find(TickCode{ this->GetLowerValue() }) : + m_ticks.ticks.end(); + if (tick_it != m_ticks.ticks.end()) { + // draw delete icon + //ImTextureID delete_icon_id = m_delete_icon_id; + ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, get_tick_pos(tick_it->tick) - icon_offset.y); + //! button_with_pos(m_delete_icon_id, icon_size, icon_pos); + if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) { + if (context.IO.MouseClicked[0]) { + // delete tick + Type type = tick_it->type; + m_ticks.ticks.erase(tick_it); + post_ticks_changed_event(type); + } + } + } + +} + +inline int hex_to_int(const char c) +{ + return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +static std::array decode_color_to_float_array(const std::string color) +{ + // set alpha to 1.0f by default + std::array ret = { 0, 0, 0, 1.0f }; + const char* c = color.data() + 1; + if (color.size() == 7 && color.front() == '#') { + for (size_t j = 0; j < 3; ++j) { + int digit1 = hex_to_int(*c++); + int digit2 = hex_to_int(*c++); + if (digit1 == -1 || digit2 == -1) break; + ret[j] = float(digit1 * 16 + digit2) / 255.0f; + } + } + return ret; +} + +void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) { + if (m_ticks.empty()) + return; + + const ImU32 blank_col = IM_COL32(255, 255, 255, 255); + + ImVec2 blank_padding = ImVec2(4.0f, 2.0f) * m_scale; + float blank_width = 1.0f * m_scale; + + ImRect blank_rect = ImRect(groove.GetCenter().x - blank_width, groove.Min.y, groove.GetCenter().x + blank_width, groove.Max.y); + + ImRect main_band = ImRect(blank_rect); + main_band.Expand(blank_padding); + + auto draw_band = [](const ImU32& clr, const ImRect& band_rc) + { + ImGui::RenderFrame(band_rc.Min, band_rc.Max, clr, false, band_rc.GetWidth() * 0.5); + //cover round corner + ImGui::RenderFrame(ImVec2(band_rc.Min.x, band_rc.Max.y - band_rc.GetWidth() * 0.5), band_rc.Max, clr, false); + }; + auto draw_main_band = [&main_band, this](const ImU32& clr) { + ImGui::RenderFrame(main_band.Min, main_band.Max, clr, false, main_band.GetWidth() * 0.5); + }; + //draw main colored band + const int default_color_idx = m_mode == MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; + std::arrayrgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]); + ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + draw_main_band(band_clr); + + static float tick_pos; + std::set::const_iterator tick_it = m_ticks.ticks.begin(); + while (tick_it != m_ticks.ticks.end()) + { + //get position from tick + tick_pos = get_pos_from_value(GetMinValue(), GetMaxValue(), tick_it->tick, slideable_region); + + ImRect band_rect = ImRect(ImVec2(main_band.Min.x, std::min(tick_pos, main_band.Min.y)), + ImVec2(main_band.Max.x, std::min(tick_pos, main_band.Max.y))); + + if (main_band.Contains(band_rect)) { + //draw colored band + if (tick_it->type == ToolChange) { + if ((m_mode == SingleExtruder) || (m_mode == MultiAsSingle)) { + const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it); + if (!clr_str.empty()) { + std::arrayrgba = decode_color_to_float_array(clr_str); + ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + if (tick_it->tick == 0) + draw_main_band(band_clr); + else + draw_band(band_clr, band_rect); + } + } + } + else if (tick_it->type == ColorChange/* && m_mode == SingleExtruder*/) { + const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it); + if (!clr_str.empty()) { + std::arrayrgba = decode_color_to_float_array(clr_str); + ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + if (tick_it->tick == 0) + draw_main_band(band_clr); + else + draw_band(band_clr, band_rect); + } + } + } + tick_it++; + } +} + +void Control::draw_label(std::string label, const ImRect& handle, const ImVec2& handle_center, bool is_horizontal /*= false*/) +{ + ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; + float rounding = 2.0f * m_scale; + ImVec2 triangle_offsets[3] = { ImVec2(2.0f, 0.0f) * m_scale, ImVec2(0.0f, 8.0f) * m_scale, ImVec2(9.0f, 0.0f) * m_scale }; + + ImVec2 text_content_size = ImGui::CalcTextSize(label.c_str()); + ImVec2 text_size = text_content_size + text_padding * 2; + ImVec2 text_start = is_horizontal ? + ImVec2(handle.Max.x + triangle_offsets[2].x, handle_center.y - text_size.y) : + ImVec2(handle.Min.x - text_size.x - triangle_offsets[2].x, handle_center.y - text_size.y) ; + ImRect text_rect(text_start, text_start + text_size); + + ImVec2 pos_1 = is_horizontal ? + ImVec2(text_rect.Min.x + triangle_offsets[0].x, text_rect.Max.y - triangle_offsets[0].y) : + text_rect.Max - triangle_offsets[0]; + ImVec2 pos_2 = is_horizontal ? pos_1 - triangle_offsets[2] : pos_1 - triangle_offsets[1]; + ImVec2 pos_3 = is_horizontal ? pos_1 - triangle_offsets[1] : pos_1 + triangle_offsets[2]; + + ImGui::RenderFrame(text_rect.Min, text_rect.Max, tooltip_bg, true, rounding); + ImGui::GetCurrentWindow()->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, tooltip_bg); + ImGui::RenderText(text_start + text_padding, label.c_str()); +}; + +bool Control::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, float scale) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& context = *GImGui; + const ImGuiID id = window->GetID(str_id); + + const ImRect draw_region(pos, pos + size); + ImGui::ItemSize(draw_region); + + float bottom_dummy = 44.0f * m_scale; + float handle_dummy_width = 17.0f * m_scale; + float text_right_dummy = 50.0f * scale * m_scale; + float groove_y = 8.0f * m_scale; + float draggable_region_y = 19.0f * m_scale; + float handle_radius = 14.0f * m_scale; + float handle_border = 2.0f * m_scale; + float rounding = 2.0f * m_scale; + float text_start_offset = 8.0f * m_scale; + ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; + float triangle_offsets[3] = { -3.5f * m_scale, 3.5f * m_scale, -6.06f * m_scale }; + + // calc groove size + ImVec2 groove_start = ImVec2(pos.x + handle_dummy_width, pos.y + size.y - groove_y - bottom_dummy); + ImVec2 groove_size = ImVec2(size.x - 2 * handle_dummy_width - text_right_dummy, groove_y); + ImRect groove = ImRect(groove_start, groove_start + groove_size); + + // set active(draggable) region. + ImRect draggable_region = ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y); + draggable_region.Expand(ImVec2(handle_radius, draggable_region_y)); + float mid_y = draggable_region.GetCenter().y; + bool hovered = ImGui::ItemHoverable(draggable_region, id); + if (hovered && context.IO.MouseDown[0]) { + ImGui::SetActiveID(id, window); + ImGui::SetFocusID(id, window); + ImGui::FocusWindow(window); + } + + // draw background + draw_background(groove); + + // set slideable region + ImRect slideable_region = draggable_region; + slideable_region.Expand(ImVec2(-handle_radius, 0)); + + // initialize the handle + float handle_pos = get_pos_from_value(v_min, v_max, *value, groove); + ImRect handle = ImRect(handle_pos - handle_radius, mid_y - handle_radius, handle_pos + handle_radius, mid_y + handle_radius); + + // update handle position and value + bool value_changed = GUI::slider_behavior(id, slideable_region, (const ImS32)v_min, (const ImS32)v_max, (ImS32*)value, &handle); + ImVec2 handle_center = handle.GetCenter(); + + // draw scroll line + ImRect scroll_line = ImRect(ImVec2(groove.Min.x, mid_y - groove_y / 2), ImVec2(handle_center.x, mid_y + groove_y / 2)); + window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr, rounding); + + // draw handle + ImGuiPureWrap::draw_hexagon(handle_center, handle_radius, handle_border_clr); + ImGuiPureWrap::draw_hexagon(handle_center, handle_radius - handle_border, handle_clr); + + draw_label(std::to_string(*value), handle, handle_center, true); + + return value_changed; +} + +bool Control::vertical_slider(const char* str_id, int* higher_value, int* lower_value, std::string& higher_label, std::string& lower_label, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, SelectedSlider& selection, bool one_layer_flag, float scale) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& context = *GImGui; + const ImGuiID id = window->GetID(str_id); + + const ImRect draw_region(pos, pos + size); + ImGui::ItemSize(draw_region); + + float right_dummy = 24.0f * m_scale; + float text_dummy_height = 34.0f * scale * m_scale; + float groove_x = 10.0f * m_scale; + float draggable_region_x = 40.0f * m_scale; + float handle_radius = 14.0f * m_scale; + float handle_border = 2.0f * m_scale; + float line_width = 2.0f * m_scale; + float line_offset = 9.0f * m_scale; + float one_handle_offset = 26.0f * m_scale; + float bar_width = 12.0f * m_scale; + ImVec2 text_content_size; + ImVec2 text_size; + + const ImU32 delete_btn_clr = IM_COL32(144, 144, 144, 255); + + // calc slider groove size + ImVec2 groove_start = ImVec2(pos.x + size.x - groove_x - right_dummy, pos.y + text_dummy_height); + ImVec2 groove_size = ImVec2(groove_x, size.y - /*2 * */text_dummy_height); + ImRect groove = ImRect(groove_start, groove_start + groove_size); + + // set active(draggable) region. + ImRect draggable_region = ImRect(groove.GetCenter().x, groove.Min.y, groove.GetCenter().x, groove.Max.y); + draggable_region.Expand(ImVec2(draggable_region_x, 0)); + float mid_x = draggable_region.GetCenter().x; + bool hovered = ImGui::ItemHoverable(draggable_region, id) && !ImGui::ItemHoverable(m_tick_rect, id); + if (hovered && context.IO.MouseDown[0]) { + ImGui::SetActiveID(id, window); + ImGui::SetFocusID(id, window); + ImGui::FocusWindow(window); + } + + // Processing interacting + // set slideable region + ImRect higher_slideable_region = ImRect(draggable_region.Min, draggable_region.Max - ImVec2(0, handle_radius)); + ImRect lower_slideable_region = ImRect(draggable_region.Min + ImVec2(0, handle_radius), draggable_region.Max); + ImRect one_slideable_region = draggable_region; + + // initialize the handles. + float higher_handle_pos = get_pos_from_value(v_min, v_max, *higher_value, higher_slideable_region); + ImRect higher_handle = ImRect(mid_x - handle_radius, higher_handle_pos - handle_radius, mid_x + handle_radius, higher_handle_pos + handle_radius); + + float lower_handle_pos = get_pos_from_value(v_min, v_max, *lower_value, lower_slideable_region); + ImRect lower_handle = ImRect(mid_x - handle_radius, lower_handle_pos - handle_radius, mid_x + handle_radius, lower_handle_pos + handle_radius); + + ImRect one_handle = ImRect(higher_handle.Min - ImVec2(one_handle_offset, 0), higher_handle.Max - ImVec2(one_handle_offset, 0)); + + //static bool become_del_handle = false; + bool value_changed = false; + const float hexagon_angle = IM_PI * 0.5; + if (!one_layer_flag) + { + // select higher handle by default + static bool h_selected = true; + if (ImGui::ItemHoverable(higher_handle, id) && context.IO.MouseClicked[0]) { + selection = ssHigher; + h_selected = true; + } + if (ImGui::ItemHoverable(lower_handle, id) && context.IO.MouseClicked[0]) { + selection = ssLower; + h_selected = false; + } + + // update handle position and value + if (h_selected) + { + value_changed = slider_behavior(id, higher_slideable_region, v_min, v_max, + higher_value, &higher_handle, ImGuiSliderFlags_Vertical, + m_tick_value, m_tick_rect); + } + if (!h_selected) { + value_changed = slider_behavior(id, lower_slideable_region, v_min, v_max, + lower_value, &lower_handle, ImGuiSliderFlags_Vertical, + m_tick_value, m_tick_rect); + } + + ImVec2 higher_handle_center = higher_handle.GetCenter(); + ImVec2 lower_handle_center = lower_handle.GetCenter(); + if (higher_handle_center.y + handle_radius > lower_handle_center.y && h_selected) + { + lower_handle = higher_handle; + lower_handle.TranslateY(handle_radius); + lower_handle_center.y = higher_handle_center.y + handle_radius; + *lower_value = *higher_value; + } + if (higher_handle_center.y + handle_radius > lower_handle_center.y && !h_selected) + { + higher_handle = lower_handle; + higher_handle.TranslateY(-handle_radius); + higher_handle_center.y = lower_handle_center.y - handle_radius; + *higher_value = *lower_value; + } + + // judge whether to open menu + if (ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) + m_show_menu = true; + if ((!ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) || + context.IO.MouseClicked[0]) + m_show_menu = false; + + ImRect scroll_line = ImRect(ImVec2(mid_x - groove_x / 2, higher_handle_center.y), ImVec2(mid_x + groove_x / 2, lower_handle_center.y)); + if (!m_ticks.empty()) { + // draw ticks + draw_ticks(h_selected ? higher_slideable_region : lower_slideable_region); + // draw background + draw_background(groove); + // draw colored band + draw_colored_band(scroll_line, h_selected ? higher_slideable_region : lower_slideable_region); + } + else { + // draw background + draw_background(groove); + // draw scroll line + window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr, 2.0f * m_scale); + } + + // draw handles + ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius, handle_border_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(lower_handle_center, handle_radius, handle_border_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(lower_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); + if (h_selected) { + ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius, handle_border_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); + window->DrawList->AddLine(higher_handle_center + ImVec2(-line_offset, 0.0f), higher_handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); + window->DrawList->AddLine(higher_handle_center + ImVec2(0.0f, -line_offset), higher_handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); + } + if (!h_selected) { + window->DrawList->AddLine(lower_handle_center + ImVec2(-line_offset, 0.0f), lower_handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); + window->DrawList->AddLine(lower_handle_center + ImVec2(0.0f, -line_offset), lower_handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); + } + + // draw higher label + draw_label(higher_label, higher_handle, higher_handle_center); + // draw lower label + draw_label(lower_label, lower_handle, lower_handle_center); + } + + if (one_layer_flag) + { + // update handle position + value_changed = GUI::slider_behavior(id, one_slideable_region, v_min, v_max, + higher_value, &one_handle, ImGuiSliderFlags_Vertical, + m_tick_value, m_tick_rect); + + ImVec2 handle_center = one_handle.GetCenter(); + + // judge whether to open menu + if (ImGui::ItemHoverable(one_handle, id) && context.IO.MouseClicked[1]) + m_show_menu = true; + if ((!ImGui::ItemHoverable(one_handle, id) && context.IO.MouseClicked[1]) || + context.IO.MouseClicked[0]) + m_show_menu = false; + + ImVec2 bar_center = higher_handle.GetCenter(); + + if (!m_ticks.empty()) { + // draw ticks + draw_ticks(one_slideable_region); + // draw colored band + draw_colored_band(groove, one_slideable_region); + } + + // draw handle + window->DrawList->AddLine(ImVec2(mid_x - bar_width, handle_center.y), ImVec2(mid_x + bar_width, handle_center.y), handle_clr, line_width); + ImGuiPureWrap::draw_hexagon(handle_center, handle_radius, handle_border_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); + window->DrawList->AddLine(handle_center + ImVec2(-line_offset, 0.0f), handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); + window->DrawList->AddLine(handle_center + ImVec2(0.0f, -line_offset), handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); + + // draw label + draw_label(higher_label, one_handle, handle_center); + } + + return value_changed; +} + +void Control::render_menu() +{ + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f) * m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, ImGui::GetStyle().ItemSpacing.y }); + + ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + + std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); + int extruder_num = colors.size(); + + if (m_show_menu) { + ImGui::OpenPopup("slider_menu_popup"); + } + + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); + if (ImGui::BeginPopup("slider_menu_popup")) { + if ((m_selection == ssLower && GetLowerValueD() == 0.0) || (m_selection == ssHigher && GetHigherValueD() == 0.0)) + { + menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, false); + } + else + { + if (menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) { + add_code_as_tick(ColorChange); + } + if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { + add_code_as_tick(PausePrint); + } + if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) { + + } + if (!gcode(Template).empty()) { + if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) { + add_code_as_tick(Template); + } + } + } + + //BBS render this menu item only when extruder_num > 1 + if (extruder_num > 1) { + if (!m_can_change_color || m_draw_mode == dmSequentialFffPrint) { + begin_menu(_u8L("Change Filament").c_str(), false); + } + else if (begin_menu(_u8L("Change Filament").c_str())) { + for (int i = 0; i < extruder_num; i++) { + std::array rgba = decode_color_to_float_array(colors[i]); + ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1); + } + end_menu(); + } + } + ImGui::EndPopup(); + } + ImGui::PopStyleVar(1); + + ImGui::PopStyleColor(1); + ImGui::PopStyleVar(3); +} + +bool Control::render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus) +{ + float scale = (float)wxGetApp().em_unit() / 10.0f; + ImGui::SameLine(66.f * scale * m_scale); + + const ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); + + const ImVec4 col = { 0.25f, 0.25f, 0.25f, 0.0f }; + ImGui::PushStyleColor(ImGuiCol_Button, col); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, col); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, col); + + std::string btn_label; + btn_label += btn_icon; + const bool ret = ImGui::Button((btn_label + "##" + label_id).c_str(), ImVec2(16*scale, 0)); + + ImGui::PopStyleColor(3); + + if (ImGui::IsItemHovered()) { + m_focus = focus; + std::string tooltip = into_u8(get_tooltip()); + ImGuiPureWrap::tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f); + } + + ImGui::PopStyleVar(); + + return ret; +} + + +bool Control::imgui_render(GUI::GLCanvas3D& canvas) +{ + bool result = false; + GUI::Size cnv_size = canvas.get_canvas_size(); + + int canvas_width = cnv_size.get_width(); + int canvas_height = cnv_size.get_height(); + + /* style and colors */ + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + int windows_flag = ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse; + +// render_input_custom_gcode(); + + float scale = (float)wxGetApp().em_unit() / 10.0f; + + if (is_horizontal()) { + float pos_x = std::max(LEFT_MARGIN, 0.2f * canvas_width); + float pos_y = (canvas_height - HORIZONTAL_SLIDER_SIZE.y * m_scale); + ImVec2 size = ImVec2(canvas_width - 2 * pos_x, HORIZONTAL_SLIDER_SIZE.y * m_scale); + ImGuiPureWrap::set_next_window_pos(pos_x, pos_y, ImGuiCond_Always); + ImGuiPureWrap::begin(std::string("moves_slider"), windows_flag); + int value = GetHigherValue(); + if (horizontal_slider("moves_slider", &value, GetMinValue(), GetMaxValue(), ImVec2(pos_x, pos_y), size, scale)) { + result = true; + SetHigherValue(value); + } + ImGuiPureWrap::end(); + } + else { + float pos_x = canvas_width - (VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale; + float pos_y = ONE_LAYER_OFFSET.y; + ImVec2 size = ImVec2((VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale, canvas_height - 4 * pos_y); + + ImGuiPureWrap::set_next_window_pos(pos_x, pos_y, ImGuiCond_Always); + ImGuiPureWrap::begin(std::string("laysers_slider"), windows_flag); + + if (!m_ticks.empty() && /*m_draw_mode == dmRegular && */render_button(ImGui::RevertButton, "revert", fiRevertIcon)) + discard_all_thicks(); + else + ImGui::NewLine(); + + render_menu(); + + int higher_value = GetHigherValue(); + int lower_value = GetLowerValue(); + std::string higher_label = into_u8(get_label(m_higher_value)); + std::string lower_label = into_u8(get_label(m_lower_value)); + int temp_higher_value = higher_value; + int temp_lower_value = lower_value; + + if (vertical_slider("laysers_slider", &higher_value, &lower_value, higher_label, lower_label, GetMinValue(), GetMaxValue(), + ImVec2(pos_x, pos_y), size, m_selection, is_one_layer(), scale)) { + if (temp_higher_value != higher_value) + SetHigherValue(higher_value); + if (temp_lower_value != lower_value) + SetLowerValue(lower_value); + result = true; + } + + ImGui::NewLine(); + if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, "one_layer", fiOneLayerIcon)) + switch_one_layer_mode(); + + ImGui::NewLine(); + if (m_draw_mode != dmSequentialGCodeView && render_button(ImGui::PrintIconMarker, "settings", fiCogIcon)) + show_cog_icon_context_menu(); + + ImGuiPureWrap::end(); + } + + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(2); + + return result; } bool Control::is_wipe_tower_layer(int tick) const @@ -743,10 +1423,10 @@ static wxString short_and_splitted_time(const std::string& time) ::sscanf(time.c_str(), "%ds", &seconds); // Format the dhm time. - auto get_d = [days]() { return format(_u8L("%1%d"), days); }; - auto get_h = [hours]() { return format(_u8L("%1%h"), hours); }; - auto get_m = [minutes](){ return format(_u8L("%1%m"), minutes); }; - auto get_s = [seconds](){ return format(_u8L("%1%s"), seconds); }; + auto get_d = [days]() { return GUI::format(_u8L("%1%d"), days); }; + auto get_h = [hours]() { return GUI::format(_u8L("%1%h"), hours); }; + auto get_m = [minutes](){ return GUI::format(_u8L("%1%m"), minutes); }; + auto get_s = [seconds](){ return GUI::format(_u8L("%1%s"), seconds); }; if (days > 0) return format_wxstr("%1%%2%\n%3%", get_d(), get_h(), get_m()); diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 1e20a4b6e8..80158c4ef1 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -303,7 +303,7 @@ public: void show_cog_icon_context_menu(); void auto_color_change(); - void imgui_render(GUI::GLCanvas3D& canvas); + bool imgui_render(GUI::GLCanvas3D& canvas); protected: @@ -476,6 +476,25 @@ private: size_t count() { return max_values.size(); } bool can_draw() { return m_parent != nullptr; } } m_ruler; + + // ImGuiDS + float m_scale{ 1.0 }; + bool m_can_change_color{ true }; + bool m_show_menu{ false }; + float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); + + void draw_background(const ImRect& groove); + void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); + void draw_label(std::string label, const ImRect& handle, const ImVec2& handle_center, bool is_horizontal = false); + void draw_ticks(const ImRect& slideable_region); + void render_menu(); + bool render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus); + bool horizontal_slider(const char* str_id, int* v, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, float scale = 1.0); + bool vertical_slider(const char* str_id, int* higher_value, int* lower_value, + std::string& higher_label, std::string& lower_label, + int v_min, int v_max, const ImVec2& pos, const ImVec2& size, + SelectedSlider& selection, bool one_layer_flag = false, float scale = 1.0f); + }; } // DoubleSlider; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index ed20c4cb64..b8f6a0965d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -352,6 +352,8 @@ void Preview::render_imgui_double_slider(GLCanvas3D& canvas) { if (m_layers_slider && m_layers_slider->IsShown()) m_layers_slider->imgui_render(canvas); + if (m_moves_slider && m_moves_slider->IsShown()) + m_moves_slider->imgui_render(canvas); } void Preview::jump_layers_slider(wxKeyEvent& evt) diff --git a/src/slic3r/GUI/ImGuiPureWrap.cpp b/src/slic3r/GUI/ImGuiPureWrap.cpp index f7179f685f..8128602fd3 100644 --- a/src/slic3r/GUI/ImGuiPureWrap.cpp +++ b/src/slic3r/GUI/ImGuiPureWrap.cpp @@ -296,6 +296,20 @@ bool combo(const std::string& label, const std::vector& options, in return res; } +void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + ImGuiWindow* window = ImGui::GetCurrentWindow(); + + float a_min = start_angle; + float a_max = start_angle + 2.f * IM_PI; + + window->DrawList->PathArcTo(center, radius, a_min, a_max, 6); + window->DrawList->PathFillConvex(col); +} + // Scroll up for one item void scroll_up() { diff --git a/src/slic3r/GUI/ImGuiPureWrap.hpp b/src/slic3r/GUI/ImGuiPureWrap.hpp index a039ad6243..5af36ace83 100644 --- a/src/slic3r/GUI/ImGuiPureWrap.hpp +++ b/src/slic3r/GUI/ImGuiPureWrap.hpp @@ -53,6 +53,8 @@ namespace ImGuiPureWrap // Use selection = -1 to not mark any option as selected bool combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f); + void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle = 0.f); + void text(const char* label); void text(const std::string& label); void text(const std::wstring& label); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 9fceabbf63..863bd90c01 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1523,6 +1523,308 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text) } } +static float accer = 1.f; + +bool slider_behavior(ImGuiID id, const ImRect& region, const ImS32 v_min, const ImS32 v_max, ImS32* out_value, ImRect* out_handle, ImGuiSliderFlags flags/* = 0*/, const int fixed_value/* = -1*/, const ImVec4& fixed_rect/* = ImRect()*/) +{ + ImGuiContext& context = *GImGui; + ImGuiIO& io = ImGui::GetIO(); + + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + + const ImVec2 handle_sz = out_handle->GetSize(); + ImS32 v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); + const float region_usable_sz = (region.Max[axis] - region.Min[axis]); + const float region_usable_pos_min = region.Min[axis]; + const float region_usable_pos_max = region.Max[axis]; + + // Process interacting with the slider + ImS32 v_new = *out_value; + bool value_changed = false; + // wheel behavior + ImRect mouse_wheel_responsive_region; + if (axis == ImGuiAxis_X) + mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(handle_sz.x / 2, 0), region.Max + ImVec2(handle_sz.x / 2, 0)); + if (axis == ImGuiAxis_Y) + mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(0, handle_sz.y), region.Max + ImVec2(0, handle_sz.y)); + if (ImGui::ItemHoverable(mouse_wheel_responsive_region, id)) { + v_new = ImClamp(*out_value + (ImS32)(context.IO.MouseWheel * accer), v_min, v_max); + } + // drag behavior + if (context.ActiveId == id) + { + float mouse_pos_ratio = 0.0f; + if (context.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (context.IO.MouseReleased[0]) + { + ImGui::ClearActiveID(); + } + if (context.IO.MouseDown[0]) + { + const float mouse_abs_pos = context.IO.MousePos[axis]; + mouse_pos_ratio = (region_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - region_usable_pos_min) / region_usable_sz, 0.0f, 1.0f) : 0.0f; + if (axis == ImGuiAxis_Y) + mouse_pos_ratio = 1.0f - mouse_pos_ratio; + v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f); + } + } + } + // click in fixed_rect behavior + if (ImGui::ItemHoverable(fixed_rect, id) && context.IO.MouseReleased[0]) + { + v_new = fixed_value; + } + + // apply result, output value + if (*out_value != v_new) + { + *out_value = v_new; + value_changed = true; + } + + // Output handle position so it can be displayed by the caller + const ImS32 v_clamped = (v_min < v_max) ? ImClamp(*out_value, v_min, v_max) : ImClamp(*out_value, v_max, v_min); + float handle_pos_ratio = v_range != 0 ? ((float)(v_clamped - v_min) / (float)v_range) : 0.0f; + handle_pos_ratio = axis == ImGuiAxis_Y ? 1.0f - handle_pos_ratio : handle_pos_ratio; + const float handle_pos = region_usable_pos_min + (region_usable_pos_max - region_usable_pos_min) * handle_pos_ratio; + + ImVec2 new_handle_center = axis == ImGuiAxis_Y ? ImVec2(out_handle->GetCenter().x, handle_pos) : ImVec2(handle_pos, out_handle->GetCenter().y); + *out_handle = ImRect(new_handle_center - handle_sz * 0.5f, new_handle_center + handle_sz * 0.5f); + + return value_changed; +} + + +bool begin_menu(const char* label, bool enabled) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None); + + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow; + + // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). + // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. + // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. + if (g.MenusIdSubmittedThisFrame.contains(id)) { + if (menu_is_open) + menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + else + g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values + return menu_is_open; + } + + // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu + g.MenusIdSubmittedThisFrame.push_back(id); + + ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + bool pressed; + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && + (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, + // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). + // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { + // Menu inside an horizontal menu bar + // Selectable extend their highlight by half ItemSpacing in each direction. + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() + popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + float w = label_size.x; + pressed = selectable(label, menu_is_open, + ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | + (!enabled ? ImGuiSelectableFlags_Disabled : 0), + ImVec2(w, 0.0f)); + ImGui::PopStyleVar(); + window->DC.CursorPos.x += IM_FLOOR( + style.ItemSpacing.x * + (-1.0f + + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else { + // Menu inside a menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w); + pressed = selectable(label, menu_is_open, + ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | + ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), + ImVec2(min_w, 0.0f)); + ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); + ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + } + + const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id); + if (menuset_is_open) g.NavWindow = backed_nav_window; + + bool want_open = false; + bool want_close = false; + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_toward_other_child_menu = false; + + ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? + g.OpenPopupStack[g.BeginPopupStack.Size].Window : + NULL; + if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { + // FIXME-DPI: Values should be derived from a master "scale" factor. + ImRect next_window_rect = child_menu_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + // GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + } + if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) + want_close = true; + + if (!menu_is_open && hovered && pressed) // Click to open + want_open = true; + else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open + want_open = true; + + if (g.NavActivateId == id) { + want_close = menu_is_open; + want_open = !menu_is_open; + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + { + want_open = true; + ImGui::NavMoveRequestCancel(); + } + } + else { + // Menu bar + if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others + { + want_open = true; + } + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + { + want_open = true; + ImGui::NavMoveRequestCancel(); + } + } + + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + ImGui::OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) ImGui::OpenPopup(label); + + if (menu_is_open) { + ImGui::SetNextWindowPos(popup_pos, + ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + else { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + } + + return menu_is_open; +} + +void end_menu() +{ + ImGui::EndMenu(); +} + +bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), + // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. + ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); + bool pressed; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { + // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. + float w = label_size.x; + window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f)); + ImGui::PopStyleVar(); + window->DC.CursorPos.x += IM_FLOOR( + style.ItemSpacing.x * + (-1.0f + + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else { + // Menu item inside a vertical menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f; + float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w); + pressed = selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + + if (icon_size.x != 0 && icon_size.y != 0) { + float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y; + float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2; + float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f; + ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y); + ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color); + } + + if (shortcut_w > 0.0f) { + ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + ImGui::PopStyleColor(); + } + if (selected) { + //ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), + // ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + } + } + + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + return pressed; +} + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index a5d1e411d9..0984d06d82 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -22,6 +22,7 @@ struct OptionViewParameters; class wxString; class wxMouseEvent; class wxKeyEvent; +struct ImRect; struct IMGUI_API ImGuiWindow; @@ -153,6 +154,11 @@ namespace ImGuiPSWrap ColorRGBA from_ImU32(const ImU32& color); ColorRGBA from_ImVec4(const ImVec4& color); } +bool slider_behavior(ImGuiID id, const ImRect& region, const ImS32 v_min, const ImS32 v_max, ImS32* out_value, ImRect* out_handle, ImGuiSliderFlags flags = 0, const int fixed_value = -1, const ImVec4& fixed_rect = ImVec4()); + +bool begin_menu(const char* label, bool enabled = true); +void end_menu(); +bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true); } // namespace GUI } // namespace Slic3r From ab9923eee640b92e8c315ccb4d7b899f4c30740d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 14 Mar 2024 02:01:52 +0100 Subject: [PATCH 03/13] ImguiDoubleSlider: WIP: Implemented "pure" ImGuiControl + For ImGuiControl implemented adding of callbacks for get_label and draw_scroll_line --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/DoubleSlider.cpp | 485 +++++--------------------- src/slic3r/GUI/DoubleSlider.hpp | 21 +- src/slic3r/GUI/GUI_Preview.cpp | 6 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 489 +++++++++++++++++++++++++++ src/slic3r/GUI/ImGuiDoubleSlider.hpp | 172 ++++++++++ src/slic3r/GUI/ImGuiWrapper.cpp | 73 ---- src/slic3r/GUI/ImGuiWrapper.hpp | 2 - 8 files changed, 754 insertions(+), 496 deletions(-) create mode 100644 src/slic3r/GUI/ImGuiDoubleSlider.cpp create mode 100644 src/slic3r/GUI/ImGuiDoubleSlider.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1ae73a1f89..89f094d448 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -254,6 +254,8 @@ set(SLIC3R_GUI_SOURCES GUI/Mouse3DController.hpp GUI/DoubleSlider.cpp GUI/DoubleSlider.hpp + GUI/ImGuiDoubleSlider.cpp + GUI/ImGuiDoubleSlider.hpp GUI/Notebook.cpp GUI/Notebook.hpp GUI/TopBar.cpp diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index db4d840327..11673d7799 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -44,11 +44,6 @@ namespace Slic3r { -using GUI::from_u8; -using GUI::into_u8; -using GUI::format_wxstr; -using GUI::format; - using namespace GUI; namespace DoubleSlider { @@ -57,19 +52,10 @@ constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar -static const float SLIDER_LENGTH = 680.0f; -static const float TEXT_WIDTH_DUMMY = 63.0f; static const ImVec2 ONE_LAYER_OFFSET = ImVec2(41.0f, /*44*/33.0f); static const ImVec2 HORIZONTAL_SLIDER_SIZE = ImVec2(764.0f, 90.0f);//764 = 680 + handle_dummy_width * 2 + text_right_dummy static const ImVec2 VERTICAL_SLIDER_SIZE = ImVec2(105.0f, 748.0f);//748 = 680 + text_dummy_height * 2 -const ImU32 tooltip_bg = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT); -const ImU32 handle_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT); -const ImU32 handle_border_clr = IM_COL32(255, 255, 255, 255);//white_bg; - -int m_tick_value = -1; -ImVec4 m_tick_rect; - bool equivalent_areas(const double& bottom_area, const double& top_area) { return fabs(bottom_area - top_area) <= miscalculation; @@ -97,7 +83,7 @@ Control::Control( wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, - const wxValidator& val, +// const wxValidator& val, const wxString& name) : wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), m_lower_value(lowerValue), @@ -178,6 +164,16 @@ Control::Control( wxWindow *parent, if (style == wxSL_VERTICAL) m_ruler.set_parent(this->GetParent()); + imgui_ctrl = ImGuiControl( lowerValue, higherValue, + minValue, maxValue, + ImVec2(0.f, 0.f), ImVec2(0.f, 0.f), + style, into_u8(name), !is_horizontal()); + + imgui_ctrl.set_get_label_cb([this](int pos) {return into_u8(get_label(pos)); }); + + if (!is_horizontal()) + imgui_ctrl.set_get_label_on_move_cb([this](int pos) {return into_u8(get_label(pos, ltEstimatedTime)); }); + } void Control::msw_rescale() @@ -249,6 +245,8 @@ void Control::SetLowerValue(const int lower_val) wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); + + imgui_ctrl.SetLowerValue(lower_val); } void Control::SetHigherValue(const int higher_val) @@ -262,6 +260,8 @@ void Control::SetHigherValue(const int higher_val) wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); + + imgui_ctrl.SetHigherValue(higher_val); } void Control::SetSelectionSpan(const int lower_val, const int higher_val) @@ -271,6 +271,9 @@ void Control::SetSelectionSpan(const int lower_val, const int higher_val) if (m_lower_value < m_higher_value) m_is_one_layer = false; + imgui_ctrl.CombineThumbs(m_is_one_layer); + imgui_ctrl.SetSelectionSpan(lower_val, higher_val); + Refresh(); Update(); @@ -284,6 +287,8 @@ void Control::SetMaxValue(const int max_value) m_max_value = max_value; Refresh(); Update(); + + imgui_ctrl.SetMaxValue(max_value); } void Control::SetSliderValues(const std::vector& values) @@ -294,6 +299,8 @@ void Control::SetSliderValues(const std::vector& values) // When "No sparce layer" is enabled, use m_layers_values for ruler update. // Because of m_values has duplicate values in this case. // m_ruler.update(this->GetParent(), m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); + + imgui_ctrl.SetSliderValues(values); } void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) @@ -420,8 +427,12 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) post_ticks_changed_event(); // init extruder sequence in respect to the extruders count - if (m_ticks.empty()) + if (m_ticks.empty()) { m_extruders_sequence.init(m_extruder_colors.size()); + imgui_ctrl.set_draw_scroll_line_cb(nullptr); + } + else + imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; @@ -585,6 +596,8 @@ void Control::render() } } +using namespace ImGui; + // ImGuiDS float Control::get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect) { @@ -600,35 +613,6 @@ float Control::get_pos_from_value(int v_min, int v_max, int value, const ImRect& return handle_pos; } -void Control::draw_background(const ImRect& groove) -{ - const ImU32 bg_rect_col = IM_COL32(255, 255, 255, 255); - const ImU32 groove_col = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); - - if (is_horizontal() || m_ticks.empty()) { - ImVec2 groove_padding = ImVec2(2.0f, 2.0f) * m_scale; - - ImRect bg_rect = groove; - bg_rect.Expand(groove_padding); - - // draw bg of slider - ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, bg_rect_col, false, 0.5 * bg_rect.GetWidth()); - // draw bg of scroll - ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth()); - } - else { - ImVec2 groove_padding = ImVec2(3.0f, 4.0f) * m_scale; - - ImRect bg_rect = groove; - bg_rect.Expand(groove_padding); - - // draw bg of slider - ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, bg_rect_col, false, bg_rect.GetWidth() * 0.5); - // draw bg of scroll - ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth()); - } -} - void Control::draw_ticks(const ImRect& slideable_region) { //if(m_draw_mode != dmRegular) // return; @@ -669,8 +653,6 @@ void Control::draw_ticks(const ImRect& slideable_region) { { ImGui::RenderFrame(tick_hover_box.Min, tick_hover_box.Max, tick_hover_box_clr, false); if (context.IO.MouseClicked[0]) { - m_tick_value = tick_it->tick; - m_tick_rect = ImVec4(tick_hover_box.Min.x, tick_hover_box.Min.y, tick_hover_box.Max.x, tick_hover_box.Max.y); } } ++tick_it; @@ -745,9 +727,7 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re if (m_ticks.empty()) return; - const ImU32 blank_col = IM_COL32(255, 255, 255, 255); - - ImVec2 blank_padding = ImVec2(4.0f, 2.0f) * m_scale; + ImVec2 blank_padding = ImVec2(5.0f, 2.0f) * m_scale; float blank_width = 1.0f * m_scale; ImRect blank_rect = ImRect(groove.GetCenter().x - blank_width, groove.Min.y, groove.GetCenter().x + blank_width, groove.Max.y); @@ -761,7 +741,7 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re //cover round corner ImGui::RenderFrame(ImVec2(band_rc.Min.x, band_rc.Max.y - band_rc.GetWidth() * 0.5), band_rc.Max, clr, false); }; - auto draw_main_band = [&main_band, this](const ImU32& clr) { + auto draw_main_band = [&main_band](const ImU32& clr) { ImGui::RenderFrame(main_band.Min, main_band.Max, clr, false, main_band.GetWidth() * 0.5); }; //draw main colored band @@ -811,286 +791,6 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re } } -void Control::draw_label(std::string label, const ImRect& handle, const ImVec2& handle_center, bool is_horizontal /*= false*/) -{ - ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; - float rounding = 2.0f * m_scale; - ImVec2 triangle_offsets[3] = { ImVec2(2.0f, 0.0f) * m_scale, ImVec2(0.0f, 8.0f) * m_scale, ImVec2(9.0f, 0.0f) * m_scale }; - - ImVec2 text_content_size = ImGui::CalcTextSize(label.c_str()); - ImVec2 text_size = text_content_size + text_padding * 2; - ImVec2 text_start = is_horizontal ? - ImVec2(handle.Max.x + triangle_offsets[2].x, handle_center.y - text_size.y) : - ImVec2(handle.Min.x - text_size.x - triangle_offsets[2].x, handle_center.y - text_size.y) ; - ImRect text_rect(text_start, text_start + text_size); - - ImVec2 pos_1 = is_horizontal ? - ImVec2(text_rect.Min.x + triangle_offsets[0].x, text_rect.Max.y - triangle_offsets[0].y) : - text_rect.Max - triangle_offsets[0]; - ImVec2 pos_2 = is_horizontal ? pos_1 - triangle_offsets[2] : pos_1 - triangle_offsets[1]; - ImVec2 pos_3 = is_horizontal ? pos_1 - triangle_offsets[1] : pos_1 + triangle_offsets[2]; - - ImGui::RenderFrame(text_rect.Min, text_rect.Max, tooltip_bg, true, rounding); - ImGui::GetCurrentWindow()->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, tooltip_bg); - ImGui::RenderText(text_start + text_padding, label.c_str()); -}; - -bool Control::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, float scale) -{ - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& context = *GImGui; - const ImGuiID id = window->GetID(str_id); - - const ImRect draw_region(pos, pos + size); - ImGui::ItemSize(draw_region); - - float bottom_dummy = 44.0f * m_scale; - float handle_dummy_width = 17.0f * m_scale; - float text_right_dummy = 50.0f * scale * m_scale; - float groove_y = 8.0f * m_scale; - float draggable_region_y = 19.0f * m_scale; - float handle_radius = 14.0f * m_scale; - float handle_border = 2.0f * m_scale; - float rounding = 2.0f * m_scale; - float text_start_offset = 8.0f * m_scale; - ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; - float triangle_offsets[3] = { -3.5f * m_scale, 3.5f * m_scale, -6.06f * m_scale }; - - // calc groove size - ImVec2 groove_start = ImVec2(pos.x + handle_dummy_width, pos.y + size.y - groove_y - bottom_dummy); - ImVec2 groove_size = ImVec2(size.x - 2 * handle_dummy_width - text_right_dummy, groove_y); - ImRect groove = ImRect(groove_start, groove_start + groove_size); - - // set active(draggable) region. - ImRect draggable_region = ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y); - draggable_region.Expand(ImVec2(handle_radius, draggable_region_y)); - float mid_y = draggable_region.GetCenter().y; - bool hovered = ImGui::ItemHoverable(draggable_region, id); - if (hovered && context.IO.MouseDown[0]) { - ImGui::SetActiveID(id, window); - ImGui::SetFocusID(id, window); - ImGui::FocusWindow(window); - } - - // draw background - draw_background(groove); - - // set slideable region - ImRect slideable_region = draggable_region; - slideable_region.Expand(ImVec2(-handle_radius, 0)); - - // initialize the handle - float handle_pos = get_pos_from_value(v_min, v_max, *value, groove); - ImRect handle = ImRect(handle_pos - handle_radius, mid_y - handle_radius, handle_pos + handle_radius, mid_y + handle_radius); - - // update handle position and value - bool value_changed = GUI::slider_behavior(id, slideable_region, (const ImS32)v_min, (const ImS32)v_max, (ImS32*)value, &handle); - ImVec2 handle_center = handle.GetCenter(); - - // draw scroll line - ImRect scroll_line = ImRect(ImVec2(groove.Min.x, mid_y - groove_y / 2), ImVec2(handle_center.x, mid_y + groove_y / 2)); - window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr, rounding); - - // draw handle - ImGuiPureWrap::draw_hexagon(handle_center, handle_radius, handle_border_clr); - ImGuiPureWrap::draw_hexagon(handle_center, handle_radius - handle_border, handle_clr); - - draw_label(std::to_string(*value), handle, handle_center, true); - - return value_changed; -} - -bool Control::vertical_slider(const char* str_id, int* higher_value, int* lower_value, std::string& higher_label, std::string& lower_label, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, SelectedSlider& selection, bool one_layer_flag, float scale) -{ - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& context = *GImGui; - const ImGuiID id = window->GetID(str_id); - - const ImRect draw_region(pos, pos + size); - ImGui::ItemSize(draw_region); - - float right_dummy = 24.0f * m_scale; - float text_dummy_height = 34.0f * scale * m_scale; - float groove_x = 10.0f * m_scale; - float draggable_region_x = 40.0f * m_scale; - float handle_radius = 14.0f * m_scale; - float handle_border = 2.0f * m_scale; - float line_width = 2.0f * m_scale; - float line_offset = 9.0f * m_scale; - float one_handle_offset = 26.0f * m_scale; - float bar_width = 12.0f * m_scale; - ImVec2 text_content_size; - ImVec2 text_size; - - const ImU32 delete_btn_clr = IM_COL32(144, 144, 144, 255); - - // calc slider groove size - ImVec2 groove_start = ImVec2(pos.x + size.x - groove_x - right_dummy, pos.y + text_dummy_height); - ImVec2 groove_size = ImVec2(groove_x, size.y - /*2 * */text_dummy_height); - ImRect groove = ImRect(groove_start, groove_start + groove_size); - - // set active(draggable) region. - ImRect draggable_region = ImRect(groove.GetCenter().x, groove.Min.y, groove.GetCenter().x, groove.Max.y); - draggable_region.Expand(ImVec2(draggable_region_x, 0)); - float mid_x = draggable_region.GetCenter().x; - bool hovered = ImGui::ItemHoverable(draggable_region, id) && !ImGui::ItemHoverable(m_tick_rect, id); - if (hovered && context.IO.MouseDown[0]) { - ImGui::SetActiveID(id, window); - ImGui::SetFocusID(id, window); - ImGui::FocusWindow(window); - } - - // Processing interacting - // set slideable region - ImRect higher_slideable_region = ImRect(draggable_region.Min, draggable_region.Max - ImVec2(0, handle_radius)); - ImRect lower_slideable_region = ImRect(draggable_region.Min + ImVec2(0, handle_radius), draggable_region.Max); - ImRect one_slideable_region = draggable_region; - - // initialize the handles. - float higher_handle_pos = get_pos_from_value(v_min, v_max, *higher_value, higher_slideable_region); - ImRect higher_handle = ImRect(mid_x - handle_radius, higher_handle_pos - handle_radius, mid_x + handle_radius, higher_handle_pos + handle_radius); - - float lower_handle_pos = get_pos_from_value(v_min, v_max, *lower_value, lower_slideable_region); - ImRect lower_handle = ImRect(mid_x - handle_radius, lower_handle_pos - handle_radius, mid_x + handle_radius, lower_handle_pos + handle_radius); - - ImRect one_handle = ImRect(higher_handle.Min - ImVec2(one_handle_offset, 0), higher_handle.Max - ImVec2(one_handle_offset, 0)); - - //static bool become_del_handle = false; - bool value_changed = false; - const float hexagon_angle = IM_PI * 0.5; - if (!one_layer_flag) - { - // select higher handle by default - static bool h_selected = true; - if (ImGui::ItemHoverable(higher_handle, id) && context.IO.MouseClicked[0]) { - selection = ssHigher; - h_selected = true; - } - if (ImGui::ItemHoverable(lower_handle, id) && context.IO.MouseClicked[0]) { - selection = ssLower; - h_selected = false; - } - - // update handle position and value - if (h_selected) - { - value_changed = slider_behavior(id, higher_slideable_region, v_min, v_max, - higher_value, &higher_handle, ImGuiSliderFlags_Vertical, - m_tick_value, m_tick_rect); - } - if (!h_selected) { - value_changed = slider_behavior(id, lower_slideable_region, v_min, v_max, - lower_value, &lower_handle, ImGuiSliderFlags_Vertical, - m_tick_value, m_tick_rect); - } - - ImVec2 higher_handle_center = higher_handle.GetCenter(); - ImVec2 lower_handle_center = lower_handle.GetCenter(); - if (higher_handle_center.y + handle_radius > lower_handle_center.y && h_selected) - { - lower_handle = higher_handle; - lower_handle.TranslateY(handle_radius); - lower_handle_center.y = higher_handle_center.y + handle_radius; - *lower_value = *higher_value; - } - if (higher_handle_center.y + handle_radius > lower_handle_center.y && !h_selected) - { - higher_handle = lower_handle; - higher_handle.TranslateY(-handle_radius); - higher_handle_center.y = lower_handle_center.y - handle_radius; - *higher_value = *lower_value; - } - - // judge whether to open menu - if (ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) - m_show_menu = true; - if ((!ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) || - context.IO.MouseClicked[0]) - m_show_menu = false; - - ImRect scroll_line = ImRect(ImVec2(mid_x - groove_x / 2, higher_handle_center.y), ImVec2(mid_x + groove_x / 2, lower_handle_center.y)); - if (!m_ticks.empty()) { - // draw ticks - draw_ticks(h_selected ? higher_slideable_region : lower_slideable_region); - // draw background - draw_background(groove); - // draw colored band - draw_colored_band(scroll_line, h_selected ? higher_slideable_region : lower_slideable_region); - } - else { - // draw background - draw_background(groove); - // draw scroll line - window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr, 2.0f * m_scale); - } - - // draw handles - ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius, handle_border_clr, hexagon_angle); - ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); - ImGuiPureWrap::draw_hexagon(lower_handle_center, handle_radius, handle_border_clr, hexagon_angle); - ImGuiPureWrap::draw_hexagon(lower_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); - if (h_selected) { - ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius, handle_border_clr, hexagon_angle); - ImGuiPureWrap::draw_hexagon(higher_handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); - window->DrawList->AddLine(higher_handle_center + ImVec2(-line_offset, 0.0f), higher_handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); - window->DrawList->AddLine(higher_handle_center + ImVec2(0.0f, -line_offset), higher_handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); - } - if (!h_selected) { - window->DrawList->AddLine(lower_handle_center + ImVec2(-line_offset, 0.0f), lower_handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); - window->DrawList->AddLine(lower_handle_center + ImVec2(0.0f, -line_offset), lower_handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); - } - - // draw higher label - draw_label(higher_label, higher_handle, higher_handle_center); - // draw lower label - draw_label(lower_label, lower_handle, lower_handle_center); - } - - if (one_layer_flag) - { - // update handle position - value_changed = GUI::slider_behavior(id, one_slideable_region, v_min, v_max, - higher_value, &one_handle, ImGuiSliderFlags_Vertical, - m_tick_value, m_tick_rect); - - ImVec2 handle_center = one_handle.GetCenter(); - - // judge whether to open menu - if (ImGui::ItemHoverable(one_handle, id) && context.IO.MouseClicked[1]) - m_show_menu = true; - if ((!ImGui::ItemHoverable(one_handle, id) && context.IO.MouseClicked[1]) || - context.IO.MouseClicked[0]) - m_show_menu = false; - - ImVec2 bar_center = higher_handle.GetCenter(); - - if (!m_ticks.empty()) { - // draw ticks - draw_ticks(one_slideable_region); - // draw colored band - draw_colored_band(groove, one_slideable_region); - } - - // draw handle - window->DrawList->AddLine(ImVec2(mid_x - bar_width, handle_center.y), ImVec2(mid_x + bar_width, handle_center.y), handle_clr, line_width); - ImGuiPureWrap::draw_hexagon(handle_center, handle_radius, handle_border_clr, hexagon_angle); - ImGuiPureWrap::draw_hexagon(handle_center, handle_radius - handle_border, handle_clr, hexagon_angle); - window->DrawList->AddLine(handle_center + ImVec2(-line_offset, 0.0f), handle_center + ImVec2(line_offset, 0.0f), handle_border_clr, line_width); - window->DrawList->AddLine(handle_center + ImVec2(0.0f, -line_offset), handle_center + ImVec2(0.0f, line_offset), handle_border_clr, line_width); - - // draw label - draw_label(higher_label, one_handle, handle_center); - } - - return value_changed; -} - void Control::render_menu() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f) * m_scale); @@ -1154,9 +854,6 @@ void Control::render_menu() bool Control::render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus) { - float scale = (float)wxGetApp().em_unit() / 10.0f; - ImGui::SameLine(66.f * scale * m_scale); - const ImGuiStyle& style = ImGui::GetStyle(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); @@ -1168,7 +865,7 @@ bool Control::render_button(const wchar_t btn_icon, const std::string& label_id, std::string btn_label; btn_label += btn_icon; - const bool ret = ImGui::Button((btn_label + "##" + label_id).c_str(), ImVec2(16*scale, 0)); + const bool ret = ImGui::Button((btn_label + "##" + label_id).c_str(), ImVec2(16*m_scale, 0)); ImGui::PopStyleColor(3); @@ -1192,81 +889,31 @@ bool Control::imgui_render(GUI::GLCanvas3D& canvas) int canvas_width = cnv_size.get_width(); int canvas_height = cnv_size.get_height(); - /* style and colors */ - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0); - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - - int windows_flag = ImGuiWindowFlags_NoTitleBar - | ImGuiWindowFlags_NoCollapse - | ImGuiWindowFlags_NoMove - | ImGuiWindowFlags_NoResize - | ImGuiWindowFlags_NoScrollbar - | ImGuiWindowFlags_NoScrollWithMouse; - -// render_input_custom_gcode(); - float scale = (float)wxGetApp().em_unit() / 10.0f; + scale *= m_scale; + ImVec2 pos; + ImVec2 size; + if (is_horizontal()) { - float pos_x = std::max(LEFT_MARGIN, 0.2f * canvas_width); - float pos_y = (canvas_height - HORIZONTAL_SLIDER_SIZE.y * m_scale); - ImVec2 size = ImVec2(canvas_width - 2 * pos_x, HORIZONTAL_SLIDER_SIZE.y * m_scale); - ImGuiPureWrap::set_next_window_pos(pos_x, pos_y, ImGuiCond_Always); - ImGuiPureWrap::begin(std::string("moves_slider"), windows_flag); - int value = GetHigherValue(); - if (horizontal_slider("moves_slider", &value, GetMinValue(), GetMaxValue(), ImVec2(pos_x, pos_y), size, scale)) { - result = true; - SetHigherValue(value); - } - ImGuiPureWrap::end(); + pos.x = std::max(LEFT_MARGIN, 0.2f * canvas_width); + pos.y = canvas_height - HORIZONTAL_SLIDER_SIZE.y * scale; + size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_SIZE.y * scale); + imgui_ctrl.ShowLabelOnMouseMove(false); } else { - float pos_x = canvas_width - (VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale; - float pos_y = ONE_LAYER_OFFSET.y; - ImVec2 size = ImVec2((VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale, canvas_height - 4 * pos_y); - - ImGuiPureWrap::set_next_window_pos(pos_x, pos_y, ImGuiCond_Always); - ImGuiPureWrap::begin(std::string("laysers_slider"), windows_flag); - - if (!m_ticks.empty() && /*m_draw_mode == dmRegular && */render_button(ImGui::RevertButton, "revert", fiRevertIcon)) - discard_all_thicks(); - else - ImGui::NewLine(); - - render_menu(); - - int higher_value = GetHigherValue(); - int lower_value = GetLowerValue(); - std::string higher_label = into_u8(get_label(m_higher_value)); - std::string lower_label = into_u8(get_label(m_lower_value)); - int temp_higher_value = higher_value; - int temp_lower_value = lower_value; - - if (vertical_slider("laysers_slider", &higher_value, &lower_value, higher_label, lower_label, GetMinValue(), GetMaxValue(), - ImVec2(pos_x, pos_y), size, m_selection, is_one_layer(), scale)) { - if (temp_higher_value != higher_value) - SetHigherValue(higher_value); - if (temp_lower_value != lower_value) - SetLowerValue(lower_value); - result = true; - } - - ImGui::NewLine(); - if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, "one_layer", fiOneLayerIcon)) - switch_one_layer_mode(); - - ImGui::NewLine(); - if (m_draw_mode != dmSequentialGCodeView && render_button(ImGui::PrintIconMarker, "settings", fiCogIcon)) - show_cog_icon_context_menu(); - - ImGuiPureWrap::end(); + pos.x = canvas_width - VERTICAL_SLIDER_SIZE.x * scale; + pos.y = ONE_LAYER_OFFSET.y; + size = ImVec2(VERTICAL_SLIDER_SIZE.x * scale, canvas_height - 4 * pos.y); + imgui_ctrl.ShowLabelOnMouseMove(true); } - ImGui::PopStyleVar(3); - ImGui::PopStyleColor(2); + imgui_ctrl.SetPos(pos); + imgui_ctrl.SetSize(size); + imgui_ctrl.SetScale(scale); + + if (imgui_ctrl.render(m_selection)) + SetSelectionSpan(m_is_one_layer ? imgui_ctrl.GetHigherValue() : imgui_ctrl.GetLowerValue(), imgui_ctrl.GetHigherValue()); return result; } @@ -2092,6 +1739,7 @@ int Control::get_tick_near_point(const wxPoint& pt) void Control::ChangeOneLayerLock() { m_is_one_layer = !m_is_one_layer; + imgui_ctrl.CombineThumbs(m_is_one_layer); m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (!m_selection) m_selection = ssHigher; @@ -2142,6 +1790,8 @@ void Control::correct_lower_value() if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) m_higher_value = m_lower_value; + + imgui_ctrl.SetSelectionSpan(m_lower_value, m_higher_value); } void Control::correct_higher_value() @@ -2153,6 +1803,8 @@ void Control::correct_higher_value() if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) m_lower_value = m_higher_value; + + imgui_ctrl.SetSelectionSpan(m_lower_value, m_higher_value); } wxString Control::get_tooltip(int tick/*=-1*/) @@ -2599,6 +2251,8 @@ void Control::OnKeyUp(wxKeyEvent &event) else if (event.GetKeyCode() == WXK_SHIFT) UseDefaultColors(true); + imgui_ctrl.CombineThumbs(m_is_one_layer); + Refresh(); Update(); event.Skip(); @@ -2649,8 +2303,8 @@ void Control::OnRightDown(wxMouseEvent& event) else m_lower_value = m_higher_value; - // set slider to "one layer" mode - m_is_right_down = m_is_one_layer = true; + // set slider to "one layer" mode // ysWhy??? + m_is_right_down = /*m_is_one_layer = */true; Refresh(); Update(); @@ -2909,8 +2563,12 @@ void Control::auto_color_change() }); } - if (m_ticks.empty()) + if (m_ticks.empty()) { GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange); + imgui_ctrl.set_draw_scroll_line_cb(nullptr); + } + else + imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); post_ticks_changed_event(); } @@ -2920,7 +2578,7 @@ void Control::OnRightUp(wxMouseEvent& event) if (!HasCapture() || m_is_left_down) return; this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; + m_is_right_down = /*m_is_one_layer = */false; if (m_mouse == maForceColorEdit) { wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); @@ -3064,6 +2722,8 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); const auto it = m_ticks.ticks.find(TickCode{ tick }); + + bool was_ticks = m_ticks.empty(); if ( it == m_ticks.ticks.end() ) { // try to add tick @@ -3078,6 +2738,13 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) else return; + if (was_ticks != m_ticks.empty()) { + if (m_ticks.empty()) + imgui_ctrl.set_draw_scroll_line_cb(nullptr); + else + imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); + } + post_ticks_changed_event(type); } @@ -3157,6 +2824,7 @@ void Control::edit_tick(int tick/* = -1*/) void Control::switch_one_layer_mode() { m_is_one_layer = !m_is_one_layer; + imgui_ctrl.CombineThumbs(m_is_one_layer); if (!m_is_one_layer) { SetLowerValue(m_min_value); SetHigherValue(m_max_value); @@ -3175,8 +2843,9 @@ void Control::discard_all_thicks() if (m_selection == ssUndef) m_selection = ssHigher; m_ticks.ticks.clear(); + imgui_ctrl.set_draw_scroll_line_cb(nullptr); post_ticks_changed_event(); - + } // Set current thumb position to the nearest tick (if it is) diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 80158c4ef1..f5e57808f2 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -9,6 +9,8 @@ #include "wxExtensions.hpp" #include "GLCanvas3D.hpp" +#include "ImGuiDoubleSlider.hpp" + #include #include #include @@ -27,6 +29,8 @@ class Layer; namespace DoubleSlider { +using namespace GUI; + /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. * So, let use same value as a permissible error for layer height. */ @@ -43,13 +47,13 @@ bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t // custom message the slider sends to its parent to notify a tick-change: wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - +/* enum SelectedSlider { ssUndef, ssLower, ssHigher }; - +*/ enum FocusedItem { fiNone, fiRevertIcon, @@ -218,7 +222,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxSL_VERTICAL, - const wxValidator& val = wxDefaultValidator, +// const wxValidator& val = wxDefaultValidator, const wxString& name = wxEmptyString); ~Control() {} @@ -483,17 +487,14 @@ private: bool m_show_menu{ false }; float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); - void draw_background(const ImRect& groove); void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); - void draw_label(std::string label, const ImRect& handle, const ImVec2& handle_center, bool is_horizontal = false); void draw_ticks(const ImRect& slideable_region); void render_menu(); bool render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus); - bool horizontal_slider(const char* str_id, int* v, int v_min, int v_max, const ImVec2& pos, const ImVec2& size, float scale = 1.0); - bool vertical_slider(const char* str_id, int* higher_value, int* lower_value, - std::string& higher_label, std::string& lower_label, - int v_min, int v_max, const ImVec2& pos, const ImVec2& size, - SelectedSlider& selection, bool one_layer_flag = false, float scale = 1.0f); + + + + GUI::ImGuiControl imgui_ctrl; }; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index b8f6a0965d..de63d7495d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -241,7 +241,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL); right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0); - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); + m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL, "moves_slider"); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -352,7 +352,7 @@ void Preview::render_imgui_double_slider(GLCanvas3D& canvas) { if (m_layers_slider && m_layers_slider->IsShown()) m_layers_slider->imgui_render(canvas); - if (m_moves_slider && m_moves_slider->IsShown()) + if (m_moves_slider && m_moves_slider->IsShown() && m_bottom_toolbar_panel->IsShown()) m_moves_slider->imgui_render(canvas); } @@ -403,7 +403,7 @@ void Preview::on_size(wxSizeEvent& evt) wxBoxSizer* Preview::create_layers_slider_sizer() { wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100); + m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxVERTICAL, "layers_slider"); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp new file mode 100644 index 0000000000..57bbd5d6c5 --- /dev/null +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -0,0 +1,489 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#include "ImGuiDoubleSlider.hpp" + +namespace Slic3r { +namespace GUI { + +const ImU32 tooltip_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT); +const ImU32 thumb_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT); +const ImU32 groove_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); +const ImU32 border_clr = IM_COL32(255, 255, 255, 255); + +static bool behavior(ImGuiID id, const ImRect& region, + const ImS32 v_min, const ImS32 v_max, + ImS32* out_value, ImRect* out_thumb, + ImGuiSliderFlags flags = 0, + bool change_on_mouse_move = false, + const int fixed_value = -1, + const ImVec4& fixed_rect = ImVec4()) +{ + ImGuiContext& context = *GImGui; + + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + + const ImVec2 thumb_sz = out_thumb->GetSize(); + ImS32 v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); + const float region_usable_sz = (region.Max[axis] - region.Min[axis]); + const float region_usable_pos_min = region.Min[axis]; + const float region_usable_pos_max = region.Max[axis]; + + const float mouse_abs_pos = context.IO.MousePos[axis]; + float mouse_pos_ratio = (region_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - region_usable_pos_min) / region_usable_sz, 0.0f, 1.0f) : 0.0f; + if (axis == ImGuiAxis_Y) + mouse_pos_ratio = 1.0f - mouse_pos_ratio; + + // Process interacting with the slider + ImS32 v_new = *out_value; + bool value_changed = false; + // wheel behavior + ImRect mouse_wheel_responsive_region; + if (axis == ImGuiAxis_X) + mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(thumb_sz.x / 2, 0), region.Max + ImVec2(thumb_sz.x / 2, 0)); + if (axis == ImGuiAxis_Y) + mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(0, thumb_sz.y), region.Max + ImVec2(0, thumb_sz.y)); + if (ImGui::ItemHoverable(mouse_wheel_responsive_region, id)) { + if (change_on_mouse_move) + v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f); + else + v_new = ImClamp(*out_value + (ImS32)(context.IO.MouseWheel/* * accer*/), v_min, v_max); + } + + // drag behavior + if (context.ActiveId == id) + { + if (context.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (context.IO.MouseReleased[0]) + ImGui::ClearActiveID(); + if (context.IO.MouseDown[0]) + v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f); + } + } + // click in fixed_rect behavior + if (ImGui::ItemHoverable(fixed_rect, id) && context.IO.MouseReleased[0]) + { + v_new = fixed_value; + } + + // apply result, output value + if (*out_value != v_new) + { + *out_value = v_new; + value_changed = true; + } + + // Output thumb position so it can be displayed by the caller + const ImS32 v_clamped = (v_min < v_max) ? ImClamp(*out_value, v_min, v_max) : ImClamp(*out_value, v_max, v_min); + float thumb_pos_ratio = v_range != 0 ? ((float)(v_clamped - v_min) / (float)v_range) : 0.0f; + thumb_pos_ratio = axis == ImGuiAxis_Y ? 1.0f - thumb_pos_ratio : thumb_pos_ratio; + const float thumb_pos = region_usable_pos_min + (region_usable_pos_max - region_usable_pos_min) * thumb_pos_ratio; + + ImVec2 new_thumb_center = axis == ImGuiAxis_Y ? ImVec2(out_thumb->GetCenter().x, thumb_pos) : ImVec2(thumb_pos, out_thumb->GetCenter().y); + *out_thumb = ImRect(new_thumb_center - thumb_sz * 0.5f, new_thumb_center + thumb_sz * 0.5f); + + return value_changed; +} + +ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) +{ + ImVec2 groove_start = is_horizontal ? + ImVec2(pos.x + thumb_dummy_sz().x, pos.y + size.y - groove_sz().y - dummy_sz().y) : + ImVec2(pos.x + size.x - groove_sz().x - dummy_sz().x, pos.y + text_dummy_sz().y); + ImVec2 groove_size = is_horizontal ? + ImVec2(size.x - 2 * thumb_dummy_sz().x - text_dummy_sz().x, groove_sz().y) : + ImVec2(groove_sz().x, size.y - 2 * text_dummy_sz().y); + + return ImRect(groove_start, groove_start + groove_size); +} + +ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is_horizontal) +{ + ImRect draggable_region = is_horizontal ? + ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y) : + ImRect(groove.GetCenter().x, groove.Min.y, groove.GetCenter().x, groove.Max.y); + draggable_region.Expand(is_horizontal ? + ImVec2(/*thumb_radius()*/0, draggable_region_sz().y) : + ImVec2(draggable_region_sz().x, 0)); + + return draggable_region; +} + +ImRect ImGuiControl::DrawOptions::slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) +{ + ImVec2 mid = draggable_region.GetCenter(); + + ImRect scroll_line = is_horizontal ? + ImRect(ImVec2(l_thumb_center.x, mid.y - groove_sz().y / 2), ImVec2(h_thumb_center.x, mid.y + groove_sz().y / 2)) : + ImRect(ImVec2(mid.x - groove_sz().x / 2, h_thumb_center.y), ImVec2(mid.x + groove_sz().x / 2, l_thumb_center.y)); + + return scroll_line; +} + +ImGuiControl::ImGuiControl( int lowerValue, + int higherValue, + int minValue, + int maxValue, + ImVec2 pos, + ImVec2 size, + long style, + std::string name, + bool use_lower_thumb) : + m_selection(ssUndef), + m_pos(pos), + m_size(size), + m_name(name), + m_lower_value(lowerValue), + m_higher_value (higherValue), + m_min_value(minValue), + m_max_value(maxValue), + m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), + m_draw_lower_thumb(use_lower_thumb) + //,m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) +{ +} + +int ImGuiControl::GetActiveValue() const +{ + return m_selection == ssLower ? m_lower_value : + m_selection == ssHigher ? m_higher_value : -1; +} + +void ImGuiControl::SetLowerValue(const int lower_val) +{ + m_selection = ssLower; + m_lower_value = lower_val; +} + +void ImGuiControl::SetHigherValue(const int higher_val) +{ + m_selection = ssHigher; + m_higher_value = higher_val; +} + +void ImGuiControl::SetSelectionSpan(const int lower_val, const int higher_val) +{ + m_lower_value = std::max(lower_val, m_min_value); + m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); +} + +void ImGuiControl::SetMaxValue(const int max_value) +{ + m_max_value = max_value; +} + +void ImGuiControl::SetSliderValues(const std::vector& values) +{ + m_values = values; +} + +std::string ImGuiControl::get_label(int pos) const +{ + if (m_cb_get_label) + return m_cb_get_label(pos); + + const size_t value = pos; + + if (m_label_koef == 1.0 && m_values.empty()) + return std::to_string(static_cast(value)); + if (value >= m_values.size()) + return "ErrVal"; + + return m_values.empty() ? + to_string_with_precision(m_label_koef * value) : + to_string_with_precision(m_values[value]); +} + +float ImGuiControl::get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect) +{ + float pos_ratio = (v_max - v_min) != 0 ? ((float)(value - v_min) / (float)(v_max - v_min)) : 0.0f; + float thumb_pos; + if (is_horizontal()) { + thumb_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio; + } + else { + pos_ratio = 1.0f - pos_ratio; + thumb_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio; + } + return thumb_pos; +} + +void ImGuiControl::draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region) +{ + if (m_cb_draw_scroll_line) + m_cb_draw_scroll_line(scroll_line, slideable_region); + else + ImGui::RenderFrame(scroll_line.Min, scroll_line.Max, thumb_bg_clr, false, m_draw_opts.rounding()); +} + +void ImGuiControl::draw_background(const ImRect& groove) +{ + ImVec2 groove_padding = (is_horizontal() ? ImVec2(2.0f, 2.0f) : ImVec2(3.0f, 4.0f)) * m_draw_opts.scale; + + ImRect bg_rect = groove; + bg_rect.Expand(groove_padding); + + // draw bg of slider + ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, border_clr, false, 0.5 * bg_rect.GetWidth()); + // draw bg of scroll + ImGui::RenderFrame(groove.Min, groove.Max, groove_bg_clr, false, 0.5 * groove.GetWidth()); +} + +void ImGuiControl::draw_label(std::string label, const ImRect& thumb) +{ + const ImVec2 thumb_center = thumb.GetCenter(); + ImVec2 text_padding = m_draw_opts.text_padding(); + float rounding = m_draw_opts.rounding(); + ImVec2 triangle_offsets[3] = { ImVec2(2.0f, 0.0f) * m_draw_opts.scale, ImVec2(0.0f, 8.0f) * m_draw_opts.scale, ImVec2(9.0f, 0.0f) * m_draw_opts.scale }; + + ImVec2 text_content_size = ImGui::CalcTextSize(label.c_str()); + ImVec2 text_size = text_content_size + text_padding * 2; + ImVec2 text_start = is_horizontal() ? + ImVec2(thumb.Max.x + triangle_offsets[2].x, thumb_center.y - text_size.y) : + ImVec2(thumb.Min.x - text_size.x - triangle_offsets[2].x, thumb_center.y - text_size.y) ; + ImRect text_rect(text_start, text_start + text_size); + + ImVec2 pos_1 = is_horizontal() ? + ImVec2(text_rect.Min.x + triangle_offsets[0].x, text_rect.Max.y - triangle_offsets[0].y) : + text_rect.Max - triangle_offsets[0]; + ImVec2 pos_2 = is_horizontal() ? pos_1 - triangle_offsets[2] : pos_1 - triangle_offsets[1]; + ImVec2 pos_3 = is_horizontal() ? pos_1 - triangle_offsets[1] : pos_1 + triangle_offsets[2]; + + ImGui::RenderFrame(text_rect.Min, text_rect.Max, tooltip_bg_clr, true, rounding); + ImGui::GetCurrentWindow()->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, tooltip_bg_clr); + ImGui::RenderText(text_start + text_padding, label.c_str()); +}; + +void ImGuiControl::draw_thumb(const ImVec2& center, bool mark/* = false*/) +{ + const float line_width = 2.0f * m_draw_opts.scale; + const float line_offset = 9.0f * m_draw_opts.scale; + const float radius = m_draw_opts.thumb_radius(); + + const float hexagon_angle = is_horizontal() ? 0.f : IM_PI * 0.5f; + + ImGuiPureWrap::draw_hexagon(center, radius, border_clr, hexagon_angle); + ImGuiPureWrap::draw_hexagon(center, radius - line_width, thumb_bg_clr, hexagon_angle); + + if (mark) { + ImGuiWindow* window = ImGui::GetCurrentWindow(); + window->DrawList->AddLine(center + ImVec2(-line_offset, 0.0f), center + ImVec2(line_offset, 0.0f), border_clr, line_width); + window->DrawList->AddLine(center + ImVec2(0.0f, -line_offset), center + ImVec2(0.0f, line_offset), border_clr, line_width); + } +} + +void ImGuiControl::apply_regions(int higher_value, int lower_value, const ImRect& draggable_region) +{ + ImVec2 mid = draggable_region.GetCenter(); + float thumb_radius = m_draw_opts.thumb_radius(); + + // set slideable region + m_regions.higher_slideable_region = is_horizontal() ? + ImRect(draggable_region.Min + ImVec2(m_draw_lower_thumb ? thumb_radius : 0, 0), draggable_region.Max) : + ImRect(draggable_region.Min, draggable_region.Max - ImVec2(0, m_combine_thumbs ? 0 : thumb_radius)); + m_regions.lower_slideable_region = is_horizontal() ? + ImRect(draggable_region.Min, draggable_region.Max - ImVec2(thumb_radius, 0)) : + ImRect(draggable_region.Min + ImVec2(0, thumb_radius), draggable_region.Max); + + // initialize the thumbs. + float higher_thumb_pos = get_pos_from_value(m_min_value, m_max_value, higher_value, m_regions.higher_slideable_region); + m_regions.higher_thumb = is_horizontal() ? + ImRect(higher_thumb_pos - thumb_radius, mid.y - thumb_radius, higher_thumb_pos + thumb_radius, mid.y + thumb_radius) : + ImRect(mid.x - thumb_radius, higher_thumb_pos - thumb_radius, mid.x + thumb_radius, higher_thumb_pos + thumb_radius); + + float lower_thumb_pos = get_pos_from_value(m_min_value, m_max_value, lower_value, m_regions.lower_slideable_region); + m_regions.lower_thumb = is_horizontal() ? + ImRect(lower_thumb_pos - thumb_radius, mid.y - thumb_radius, lower_thumb_pos + thumb_radius, mid.y + thumb_radius) : + ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius); +} + +void ImGuiControl::check_thumbs(int* higher_value, int* lower_value) +{ + if (!m_draw_lower_thumb || m_combine_thumbs) + return; + + const ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter(); + const ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter(); + const float thumb_radius = m_draw_opts.thumb_radius(); + + const float higher_thumb_center_pos = is_horizontal() ? higher_thumb_center.x : higher_thumb_center.y; + const float lower_thumb_center_pos = is_horizontal() ? lower_thumb_center.x : lower_thumb_center.y; + + if (is_horizontal()) { + if (lower_thumb_center_pos + thumb_radius > higher_thumb_center_pos) { + if (m_selection == ssHigher) { + m_regions.lower_thumb = m_regions.higher_thumb; + m_regions.higher_thumb.TranslateX(thumb_radius); + *lower_value = *higher_value; + } + else { + m_regions.higher_thumb = m_regions.lower_thumb; + m_regions.lower_thumb.TranslateX(-thumb_radius); + *higher_value = *lower_value; + } + } + } + else { + if (higher_thumb_center_pos + thumb_radius > lower_thumb_center_pos) { + if (m_selection == ssHigher) { + m_regions.lower_thumb = m_regions.higher_thumb; + m_regions.lower_thumb.TranslateY(thumb_radius); + *lower_value = *higher_value; + } + else { + m_regions.higher_thumb = m_regions.lower_thumb; + m_regions.lower_thumb.TranslateY(-thumb_radius); + *higher_value = *lower_value; + } + } + } +} + +bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, + std::string& higher_label, std::string& lower_label, + const ImVec2& pos, const ImVec2& size, float scale) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& context = *GImGui; + const ImGuiID id = window->GetID(m_name.c_str()); + + const ImRect draw_region(pos, pos + size); + ImGui::ItemSize(draw_region); + + // calc slider groove size + ImRect groove = m_draw_opts.groove(pos, size, is_horizontal()); + + // set active(draggable) region. + ImRect draggable_region = m_draw_opts.draggable_region(groove, is_horizontal()); + + if (ImGui::ItemHoverable(draggable_region, id) && context.IO.MouseDown[0]) { + ImGui::SetActiveID(id, window); + ImGui::SetFocusID(id, window); + ImGui::FocusWindow(window); + } + + // set slideable region and thumbs. + apply_regions(*higher_value, *lower_value, draggable_region); + + // Processing interacting + + if (ImGui::ItemHoverable(m_regions.higher_thumb, id) && context.IO.MouseClicked[0]) + m_selection = ssHigher; + + if (m_draw_lower_thumb && !m_combine_thumbs && + ImGui::ItemHoverable(m_regions.lower_thumb, id) && context.IO.MouseClicked[0]) + m_selection = ssLower; + + // update thumb position and value + bool value_changed = false; + if (m_selection == ssHigher) + value_changed = behavior(id, m_regions.higher_slideable_region, m_min_value, m_max_value, + higher_value, &m_regions.higher_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); + else if (m_draw_lower_thumb && !m_combine_thumbs) + value_changed = behavior(id, m_regions.lower_slideable_region, m_min_value, m_max_value, + lower_value, &m_regions.lower_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); + + bool show_move_label = false; + ImRect mouse_pos_rc = m_regions.higher_thumb; + if (!value_changed && ImGui::ItemHoverable(draw_region, id)) { + behavior(id, draggable_region, m_min_value, m_max_value, + &m_mouse_pos_value, &mouse_pos_rc, is_horizontal() ? 0 : ImGuiSliderFlags_Vertical, true); + show_move_label = true; + } + + // check thumbs values and correct them if needed + check_thumbs(higher_value, lower_value); + + // detect right click on thumb + if (ImGui::ItemHoverable(m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb, id) && context.IO.MouseClicked[1]) + m_rclick_on_selected_thumb = true; + if ((!ImGui::ItemHoverable(m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb, id) && context.IO.MouseClicked[1]) || + context.IO.MouseClicked[0]) + m_rclick_on_selected_thumb = false; + + ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter(); + ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter(); + + ImRect scroll_line = m_draw_opts.slider_line(draggable_region, higher_thumb_center, lower_thumb_center, is_horizontal()); + + // draw background + draw_background(groove); + // draw scroll line + draw_scroll_line(m_combine_thumbs ? groove : scroll_line, groove); + + // draw thumbs with label + // select and mark higher thumb by default + draw_thumb(higher_thumb_center, m_selection != ssLower && m_draw_lower_thumb); + draw_label(higher_label, m_regions.higher_thumb); + + if (m_draw_lower_thumb && !m_combine_thumbs) { + draw_thumb(lower_thumb_center, m_selection == ssLower); + draw_label(lower_label, m_regions.lower_thumb); + } + + // draw label on mouse move + if (show_move_label) + draw_label(get_label_on_move(m_mouse_pos_value), mouse_pos_rc); + + return value_changed; +} + +bool ImGuiControl::render(SelectedSlider& selection) +{ + bool result = false; + + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + + ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + int windows_flag = ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse; + + ImGuiPureWrap::set_next_window_pos(m_pos.x, m_pos.y, ImGuiCond_Always); + ImGuiPureWrap::begin(m_name, windows_flag); + + float scale = 1.f; + + int higher_value = m_higher_value; + int lower_value = m_lower_value; + std::string higher_label = get_label(m_higher_value); + std::string lower_label = get_label(m_lower_value); + int temp_higher_value = m_higher_value; + int temp_lower_value = m_lower_value; + + if (draw_slider(&higher_value, &lower_value, higher_label, lower_label, m_pos, m_size, scale)) { + if (temp_higher_value != higher_value) + SetHigherValue(higher_value); + if (temp_lower_value != lower_value) + SetLowerValue(lower_value); + result = true; + } + selection = m_selection; + + ImGuiPureWrap::end(); + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(3); + + return result; +} + +//} // DoubleSlider + +} // GUI + +} // Slic3r + diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp new file mode 100644 index 0000000000..732ceff57d --- /dev/null +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -0,0 +1,172 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_ImGUI_DoubleSlider_hpp_ +#define slic3r_ImGUI_DoubleSlider_hpp_ + +#include "ImGuiPureWrap.hpp" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include "imgui/imgui_internal.h" + +#include + +// this code is borrowed from https://stackoverflow.com/questions/16605967/set-precision-of-stdto-string-when-converting-floating-point-values +template +std::string to_string_with_precision(const T a_value, const int n = 2) +{ + std::ostringstream out; + out.precision(n); + out << std::fixed << a_value; + return std::move(out).str(); +} + +namespace Slic3r { +namespace GUI { + +enum SelectedSlider { + ssUndef, + ssLower, + ssHigher +}; + +enum LabelType +{ + ltHeightWithLayer, + ltHeight, + ltEstimatedTime, +}; + +class ImGuiControl +{ +public: + ImGuiControl(int lowerValue, + int higherValue, + int minValue, + int maxValue, + ImVec2 pos, + ImVec2 size, + long style = wxSL_VERTICAL, + std::string name = "d_slider", + bool use_lower_thumb = true); + ImGuiControl() {} + ~ImGuiControl() {} + + int GetMinValue() const { return m_min_value; } + int GetMaxValue() const { return m_max_value; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } + int GetLowerValue() const { return m_lower_value; } + int GetHigherValue() const { return m_higher_value; } + int GetActiveValue() const; + + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetLowerValue (const int lower_val); + void SetHigherValue(const int higher_val); + void SetSelectionSpan(const int lower_val, const int higher_val); + + void SetMaxValue(const int max_value); + void SetSliderValues(const std::vector& values); + + void SetPos(ImVec2 pos) { m_pos = pos; } + void SetSize(ImVec2 size) { m_size = size; } + void SetScale(float scale) { m_draw_opts.scale = scale; } + void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } + void CombineThumbs(bool combine = true) { m_combine_thumbs = combine; } + + void set_get_label_on_move_cb(std::function cb) { m_cb_get_label_on_move = cb; } + void set_get_label_cb(std::function cb) { m_cb_get_label = cb; } + void set_draw_scroll_line_cb(std::function cb) { m_cb_draw_scroll_line = cb; } + + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_lower_at_min() const { return m_lower_value == m_min_value; } + bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + + bool render(SelectedSlider& selection); + void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); + + std::string get_label(int pos) const; + std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } + + struct DrawOptions { + float scale { 1.f }; // used for Retina on osx + + ImVec2 dummy_sz() { return ImVec2(24.0f, 44.0f) * scale; } + ImVec2 thumb_dummy_sz() { return ImVec2(17.0f, 17.0f) * scale; } + ImVec2 groove_sz() { return ImVec2(10.0f, 8.0f) * scale; } + ImVec2 draggable_region_sz() { return ImVec2(40.0f, 19.0f) * scale; } + ImVec2 text_dummy_sz() { return ImVec2(50.0f, 34.0f) * scale; } + ImVec2 text_padding() { return ImVec2( 5.0f, 2.0f) * scale; } + + float thumb_radius() { return 14.0f * scale; } + float thumb_border() { return 2.0f * scale; } + float rounding() { return 2.0f * scale; } + + ImRect groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal); + ImRect draggable_region(const ImRect& groove, bool is_horizontal); + ImRect slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal); + }; + + struct Regions { + ImRect higher_slideable_region; + ImRect lower_slideable_region; + ImRect higher_thumb; + ImRect lower_thumb; + }; + +private: + + SelectedSlider m_selection; + ImVec2 m_pos; + ImVec2 m_size; + std::string m_name; + + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + int m_mouse_pos_value; + + long m_style; + double m_label_koef{ 1. }; + + bool m_lclick_on_selected_thumb{ false }; + bool m_rclick_on_selected_thumb{ false }; + + bool m_draw_lower_thumb{ true }; + bool m_combine_thumbs { false }; + bool m_show_move_label{ false }; + + std::function m_cb_get_label { nullptr }; + std::function m_cb_get_label_on_move { nullptr }; + std::function m_cb_draw_scroll_line { nullptr }; + + void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); + + float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); + void check_thumbs(int* higher_value, int* lower_value); + + void draw_background(const ImRect& groove); + void draw_label(std::string label, const ImRect& thumb); + void draw_thumb(const ImVec2& center, bool mark = false); + bool draw_slider(int* higher_value, int* lower_value, + std::string& higher_label, std::string& lower_label, + const ImVec2& pos, const ImVec2& size, float scale = 1.0f); + +protected: + + std::vector m_values; + + DrawOptions m_draw_opts; + Regions m_regions; +}; + +} // GUI + +} // Slic3r + + +#endif // slic3r_ImGUI_DoubleSlider_hpp_ diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 863bd90c01..455e961c00 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1523,79 +1523,6 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text) } } -static float accer = 1.f; - -bool slider_behavior(ImGuiID id, const ImRect& region, const ImS32 v_min, const ImS32 v_max, ImS32* out_value, ImRect* out_handle, ImGuiSliderFlags flags/* = 0*/, const int fixed_value/* = -1*/, const ImVec4& fixed_rect/* = ImRect()*/) -{ - ImGuiContext& context = *GImGui; - ImGuiIO& io = ImGui::GetIO(); - - const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; - - const ImVec2 handle_sz = out_handle->GetSize(); - ImS32 v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); - const float region_usable_sz = (region.Max[axis] - region.Min[axis]); - const float region_usable_pos_min = region.Min[axis]; - const float region_usable_pos_max = region.Max[axis]; - - // Process interacting with the slider - ImS32 v_new = *out_value; - bool value_changed = false; - // wheel behavior - ImRect mouse_wheel_responsive_region; - if (axis == ImGuiAxis_X) - mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(handle_sz.x / 2, 0), region.Max + ImVec2(handle_sz.x / 2, 0)); - if (axis == ImGuiAxis_Y) - mouse_wheel_responsive_region = ImRect(region.Min - ImVec2(0, handle_sz.y), region.Max + ImVec2(0, handle_sz.y)); - if (ImGui::ItemHoverable(mouse_wheel_responsive_region, id)) { - v_new = ImClamp(*out_value + (ImS32)(context.IO.MouseWheel * accer), v_min, v_max); - } - // drag behavior - if (context.ActiveId == id) - { - float mouse_pos_ratio = 0.0f; - if (context.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (context.IO.MouseReleased[0]) - { - ImGui::ClearActiveID(); - } - if (context.IO.MouseDown[0]) - { - const float mouse_abs_pos = context.IO.MousePos[axis]; - mouse_pos_ratio = (region_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - region_usable_pos_min) / region_usable_sz, 0.0f, 1.0f) : 0.0f; - if (axis == ImGuiAxis_Y) - mouse_pos_ratio = 1.0f - mouse_pos_ratio; - v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f); - } - } - } - // click in fixed_rect behavior - if (ImGui::ItemHoverable(fixed_rect, id) && context.IO.MouseReleased[0]) - { - v_new = fixed_value; - } - - // apply result, output value - if (*out_value != v_new) - { - *out_value = v_new; - value_changed = true; - } - - // Output handle position so it can be displayed by the caller - const ImS32 v_clamped = (v_min < v_max) ? ImClamp(*out_value, v_min, v_max) : ImClamp(*out_value, v_max, v_min); - float handle_pos_ratio = v_range != 0 ? ((float)(v_clamped - v_min) / (float)v_range) : 0.0f; - handle_pos_ratio = axis == ImGuiAxis_Y ? 1.0f - handle_pos_ratio : handle_pos_ratio; - const float handle_pos = region_usable_pos_min + (region_usable_pos_max - region_usable_pos_min) * handle_pos_ratio; - - ImVec2 new_handle_center = axis == ImGuiAxis_Y ? ImVec2(out_handle->GetCenter().x, handle_pos) : ImVec2(handle_pos, out_handle->GetCenter().y); - *out_handle = ImRect(new_handle_center - handle_sz * 0.5f, new_handle_center + handle_sz * 0.5f); - - return value_changed; -} - - bool begin_menu(const char* label, bool enabled) { ImGuiWindow* window = ImGui::GetCurrentWindow(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 0984d06d82..56797f7e3a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -154,8 +154,6 @@ namespace ImGuiPSWrap ColorRGBA from_ImU32(const ImU32& color); ColorRGBA from_ImVec4(const ImVec4& color); } -bool slider_behavior(ImGuiID id, const ImRect& region, const ImS32 v_min, const ImS32 v_max, ImS32* out_value, ImRect* out_handle, ImGuiSliderFlags flags = 0, const int fixed_value = -1, const ImVec4& fixed_rect = ImVec4()); - bool begin_menu(const char* label, bool enabled = true); void end_menu(); bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true); From 8e749734ff4a58bb36fc76736258e7bb623a1e1a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Mar 2024 00:49:00 +0100 Subject: [PATCH 04/13] ImguiDoubleSlider: WIP: For vertical slider are added : * actions buttons and theirs process * rendering of ticks and theirs info buttons * show label with estimated time on mouse moving + ImGuiWrapper::image_button is extended for highlight_on_hover parameter to enable/disable highlighting on the hover + Show and process some icon/buttons in respect to the application mode (if is Editor or Viewer) --- resources/icons/cog_f.svg | 17 +++ resources/icons/undo_f.svg | 12 ++ src/imgui/imconfig.h | 21 ++- src/slic3r/GUI/DoubleSlider.cpp | 186 +++++++++++++++++---------- src/slic3r/GUI/DoubleSlider.hpp | 3 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 75 +++++++---- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 18 +-- src/slic3r/GUI/ImGuiWrapper.cpp | 37 +++++- src/slic3r/GUI/ImGuiWrapper.hpp | 3 +- 9 files changed, 259 insertions(+), 113 deletions(-) create mode 100644 resources/icons/cog_f.svg create mode 100644 resources/icons/undo_f.svg diff --git a/resources/icons/cog_f.svg b/resources/icons/cog_f.svg new file mode 100644 index 0000000000..28267ddb4b --- /dev/null +++ b/resources/icons/cog_f.svg @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/resources/icons/undo_f.svg b/resources/icons/undo_f.svg new file mode 100644 index 0000000000..c2db835498 --- /dev/null +++ b/resources/icons/undo_f.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index ba3cd1c368..172db0ec95 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -153,8 +153,6 @@ namespace ImGui const wchar_t PlugMarker = 0x1C; const wchar_t DowelMarker = 0x1D; const wchar_t SnapMarker = 0x1E; - const wchar_t Lock = 0x1F; - const wchar_t Unlock = 0x17; const wchar_t HorizontalHide = 0xB1; const wchar_t HorizontalShow = 0xB2; // Do not forget use following letters only in wstring @@ -191,6 +189,25 @@ namespace ImGui const wchar_t InfoMarkerSmall = 0x2716; const wchar_t CollapseBtn = 0x2715; + // icons for double slider (middle size icons) + const wchar_t Lock = 0x2801; + const wchar_t LockHovered = 0x2802; + const wchar_t Unlock = 0x2803; + const wchar_t UnlockHovered = 0x2804; + const wchar_t DSRevert = 0x2805; + const wchar_t DSRevertHovered = 0x2806; + const wchar_t DSSettings = 0x2807; + const wchar_t DSSettingsHovered = 0x2808; + // icons for double slider (small size icons) + const wchar_t ErrorTick = 0x2809; + const wchar_t ErrorTickHovered = 0x280A; + const wchar_t PausePrint = 0x280B; + const wchar_t PausePrintHovered = 0x280C; + const wchar_t EditGCode = 0x280D; + const wchar_t EditGCodeHovered = 0x280E; + const wchar_t RemoveTick = 0x280F; + const wchar_t RemoveTickHovered = 0x2810; + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 11673d7799..55e3eafe0e 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -52,6 +52,8 @@ constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar +static const float BTN_SZ = 24.f; + static const ImVec2 ONE_LAYER_OFFSET = ImVec2(41.0f, /*44*/33.0f); static const ImVec2 HORIZONTAL_SLIDER_SIZE = ImVec2(764.0f, 90.0f);//764 = 680 + handle_dummy_width * 2 + text_right_dummy static const ImVec2 VERTICAL_SLIDER_SIZE = ImVec2(105.0f, 748.0f);//748 = 680 + text_dummy_height * 2 @@ -91,6 +93,7 @@ Control::Control( wxWindow *parent, m_min_value(minValue), m_max_value(maxValue), m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), + m_allow_editing(wxGetApp().is_editor()), m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) { #ifdef __WXOSX__ @@ -171,8 +174,10 @@ Control::Control( wxWindow *parent, imgui_ctrl.set_get_label_cb([this](int pos) {return into_u8(get_label(pos)); }); - if (!is_horizontal()) + if (!is_horizontal()) { imgui_ctrl.set_get_label_on_move_cb([this](int pos) {return into_u8(get_label(pos, ltEstimatedTime)); }); + imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); + } } @@ -613,7 +618,8 @@ float Control::get_pos_from_value(int v_min, int v_max, int value, const ImRect& return handle_pos; } -void Control::draw_ticks(const ImRect& slideable_region) { +void Control::draw_ticks(const ImRect& slideable_region) +{ //if(m_draw_mode != dmRegular) // return; //if (m_ticks.empty() || m_mode == MultiExtruder) @@ -623,22 +629,22 @@ void Control::draw_ticks(const ImRect& slideable_region) { ImGuiContext& context = *GImGui; - ImVec2 tick_box = ImVec2(46.0f, 16.0f) * m_scale; - ImVec2 tick_offset = ImVec2(19.0f, 11.0f) * m_scale; - float tick_width = 1.0f * m_scale; - ImVec2 icon_offset = ImVec2(13.0f, 7.0f) * m_scale; - ImVec2 icon_size = ImVec2(14.0f, 14.0f) * m_scale; + const ImVec2 tick_border = ImVec2(23.0f, 2.0f) * m_scale; + // distance form center begin end + const ImVec2 tick_size = ImVec2(19.0f, 11.0f) * m_scale; + const float tick_width = 1.0f * m_scale; + const float icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; + const float icon_offset = 0.5f * icon_side;; - const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); //IM_COL32(144, 144, 144, 255); - const ImU32 tick_hover_box_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); //IM_COL32(219, 253, 231, 255); - const ImU32 delete_btn_clr = IM_COL32(144, 144, 144, 255); + const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); + const ImU32 tick_hovered_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); auto get_tick_pos = [this, slideable_region](int tick) - { - int v_min = GetMinValue(); - int v_max = GetMaxValue(); - return get_pos_from_value(v_min, v_max, tick, slideable_region); - }; + { + int v_min = GetMinValue(); + int v_max = GetMaxValue(); + return get_pos_from_value(v_min, v_max, tick, slideable_region); + }; std::set::const_iterator tick_it = m_ticks.ticks.begin(); while (tick_it != m_ticks.ticks.end()) @@ -646,17 +652,19 @@ void Control::draw_ticks(const ImRect& slideable_region) { float tick_pos = get_tick_pos(tick_it->tick); //draw tick hover box when hovered - ImRect tick_hover_box = ImRect(slideable_region.GetCenter().x - tick_box.x / 2, tick_pos - tick_box.y / 2, slideable_region.GetCenter().x + tick_box.x / 2, - tick_pos + tick_box.y / 2); + ImRect tick_hover_box = ImRect(slideable_region.GetCenter().x - tick_border.x, tick_pos - tick_border.y, + slideable_region.GetCenter().x + tick_border.x, tick_pos + tick_border.y - tick_width); - if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max)) - { - ImGui::RenderFrame(tick_hover_box.Min, tick_hover_box.Max, tick_hover_box_clr, false); - if (context.IO.MouseClicked[0]) { - } + if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max)) { + ImGui::RenderFrame(tick_hover_box.Min, tick_hover_box.Max, tick_hovered_clr, false); + break; } ++tick_it; } + + auto active_tick_it = m_selection == ssHigher ? m_ticks.ticks.find(TickCode{ this->GetHigherValue() }) : + m_selection == ssLower ? m_ticks.ticks.find(TickCode{ this->GetLowerValue() }) : + m_ticks.ticks.end(); tick_it = m_ticks.ticks.begin(); while (tick_it != m_ticks.ticks.end()) @@ -664,42 +672,43 @@ void Control::draw_ticks(const ImRect& slideable_region) { float tick_pos = get_tick_pos(tick_it->tick); //draw ticks - ImRect tick_left = ImRect(slideable_region.GetCenter().x - tick_offset.x, tick_pos - tick_width, slideable_region.GetCenter().x - tick_offset.y, tick_pos); - ImRect tick_right = ImRect(slideable_region.GetCenter().x + tick_offset.y, tick_pos - tick_width, slideable_region.GetCenter().x + tick_offset.x, tick_pos); + ImRect tick_left = ImRect(slideable_region.GetCenter().x - tick_size.x, tick_pos - tick_width, slideable_region.GetCenter().x - tick_size.y, tick_pos); + ImRect tick_right = ImRect(slideable_region.GetCenter().x + tick_size.y, tick_pos - tick_width, slideable_region.GetCenter().x + tick_size.x, tick_pos); ImGui::RenderFrame(tick_left.Min, tick_left.Max, tick_clr, false); ImGui::RenderFrame(tick_right.Min, tick_right.Max, tick_clr, false); - //draw pause icon - if (tick_it->type == PausePrint) { - //ImTextureID pause_icon_id = m_pause_icon_id; - ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, tick_pos - icon_offset.y); - //button_with_pos(pause_icon_id, icon_size, icon_pos); - if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) { - if (context.IO.MouseClicked[0]) - int a = 0; - } - } - ++tick_it; - } + ImVec2 icon_pos = ImVec2(tick_right.Max.x + icon_offset, tick_pos - icon_offset); + std::string btn_label = "tick " + std::to_string(tick_it->tick); - tick_it = m_selection == ssHigher ? m_ticks.ticks.find(TickCode{ this->GetHigherValue() }) : - m_selection == ssLower ? m_ticks.ticks.find(TickCode{ this->GetLowerValue() }) : - m_ticks.ticks.end(); - if (tick_it != m_ticks.ticks.end()) { - // draw delete icon - //ImTextureID delete_icon_id = m_delete_icon_id; - ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, get_tick_pos(tick_it->tick) - icon_offset.y); - //! button_with_pos(m_delete_icon_id, icon_size, icon_pos); - if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) { - if (context.IO.MouseClicked[0]) { - // delete tick + //draw tick icon-buttons + bool activate_this_tick = false; + if (tick_it == active_tick_it && m_allow_editing) { + // delete tick + if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, m_selection == ssHigher ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { Type type = tick_it->type; m_ticks.ticks.erase(tick_it); post_ticks_changed_event(type); + break; } + } + else if (m_draw_mode != dmRegular)// if we have non-regular draw mode, all ticks should be marked with error icon + activate_this_tick = render_button(ImGui::ErrorTick, ImGui::ErrorTickHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); + else if (tick_it->type == ColorChange || tick_it->type == ToolChange) { + if (m_ticks.is_conflict_tick(*tick_it, m_mode, m_only_extruder, m_values[tick_it->tick])) + activate_this_tick = render_button(ImGui::ErrorTick, ImGui::ErrorTickHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); } - } + else if (tick_it->type == PausePrint) + activate_this_tick = render_button(ImGui::PausePrint, ImGui::PausePrintHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); + else + activate_this_tick = render_button(ImGui::EditGCode, ImGui::EditGCodeHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); + if (activate_this_tick) { + m_selection == ssHigher ? SetHigherValue(tick_it->tick) : SetLowerValue(tick_it->tick); + break; + } + + ++tick_it; + } } inline int hex_to_int(const char c) @@ -802,7 +811,7 @@ void Control::render_menu() std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); int extruder_num = colors.size(); - if (m_show_menu) { + if (imgui_ctrl.is_rclick_on_thumb()) { ImGui::OpenPopup("slider_menu_popup"); } @@ -815,13 +824,14 @@ void Control::render_menu() else { if (menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) { + UseDefaultColors(false); add_code_as_tick(ColorChange); } if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { add_code_as_tick(PausePrint); } if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) { - + add_code_as_tick(Custom); } if (!gcode(Template).empty()) { if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) { @@ -852,30 +862,39 @@ void Control::render_menu() ImGui::PopStyleVar(3); } -bool Control::render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus) +bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) { - const ImGuiStyle& style = ImGui::GetStyle(); + float scale = (float)wxGetApp().em_unit() / 10.0f; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - const ImVec4 col = { 0.25f, 0.25f, 0.25f, 0.0f }; - ImGui::PushStyleColor(ImGuiCol_Button, col); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, col); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, col); + ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - std::string btn_label; - btn_label += btn_icon; - const bool ret = ImGui::Button((btn_label + "##" + label_id).c_str(), ImVec2(16*m_scale, 0)); + int windows_flag = ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse; - ImGui::PopStyleColor(3); + auto m_imgui = wxGetApp().imgui(); + ImGuiPureWrap::set_next_window_pos(pos.x, pos.y, ImGuiCond_Always); + std::string win_name = label_id + "##btn_win"; + ImGuiPureWrap::begin(win_name, windows_flag); - if (ImGui::IsItemHovered()) { - m_focus = focus; - std::string tooltip = into_u8(get_tooltip()); - ImGuiPureWrap::tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f); - } + ImGuiContext& g = *GImGui; - ImGui::PopStyleVar(); + std::string tooltip = m_allow_editing ? into_u8(get_tooltip(tick)) : ""; + ImGui::SetCursorPos(ImVec2(0, 0)); + const bool ret = m_imgui->image_button(g.HoveredWindow == g.CurrentWindow ? btn_icon_hovered : btn_icon, tooltip, false); + + ImGuiPureWrap::end(); + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(3); return ret; } @@ -902,7 +921,9 @@ bool Control::imgui_render(GUI::GLCanvas3D& canvas) imgui_ctrl.ShowLabelOnMouseMove(false); } else { - pos.x = canvas_width - VERTICAL_SLIDER_SIZE.x * scale; + const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; + + pos.x = canvas_width - VERTICAL_SLIDER_SIZE.x * scale - tick_icon_side; pos.y = ONE_LAYER_OFFSET.y; size = ImVec2(VERTICAL_SLIDER_SIZE.x * scale, canvas_height - 4 * pos.y); imgui_ctrl.ShowLabelOnMouseMove(true); @@ -912,9 +933,28 @@ bool Control::imgui_render(GUI::GLCanvas3D& canvas) imgui_ctrl.SetSize(size); imgui_ctrl.SetScale(scale); + const float btn_sz = BTN_SZ*scale; + ImVec2 btn_pos = ImVec2(pos.x + 2.7 * btn_sz, pos.y - 0.5 * btn_sz); + + if (!is_horizontal() && !m_ticks.empty() && m_allow_editing && + render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) + discard_all_thicks(); + if (imgui_ctrl.render(m_selection)) SetSelectionSpan(m_is_one_layer ? imgui_ctrl.GetHigherValue() : imgui_ctrl.GetLowerValue(), imgui_ctrl.GetHigherValue()); + if (!is_horizontal()) { + btn_pos.y += 0.5 * btn_sz + size.y; + if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, is_one_layer() ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) + switch_one_layer_mode(); + + btn_pos.y += btn_sz; + if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) + show_cog_icon_context_menu(); + + if (m_allow_editing) + render_menu(); + } return result; } @@ -1739,9 +1779,11 @@ int Control::get_tick_near_point(const wxPoint& pt) void Control::ChangeOneLayerLock() { m_is_one_layer = !m_is_one_layer; - imgui_ctrl.CombineThumbs(m_is_one_layer); + imgui_ctrl.CombineThumbs(m_is_one_layer); + if (!m_selection || m_is_one_layer) + m_selection = ssHigher; m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; + // if (!m_selection) m_selection = ssHigher; Refresh(); Update(); @@ -2829,8 +2871,10 @@ void Control::switch_one_layer_mode() SetLowerValue(m_min_value); SetHigherValue(m_max_value); } + if (!m_selection || m_is_one_layer) + m_selection = ssHigher; m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (m_selection == ssUndef) m_selection = ssHigher; +// if (m_selection == ssUndef) m_selection = ssHigher; } // discard all custom changes on DoubleSlider diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index f5e57808f2..b021c774f9 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -381,6 +381,7 @@ private: int m_higher_value; bool m_render_as_disabled{ false }; + bool m_allow_editing{ true }; ScalableBitmap m_bmp_thumb_higher; ScalableBitmap m_bmp_thumb_lower; @@ -490,7 +491,7 @@ private: void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); void render_menu(); - bool render_button(const wchar_t btn_icon, const std::string& label_id, FocusedItem focus); + bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index 57bbd5d6c5..f06ab7eb98 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -95,7 +95,7 @@ ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, ImVec2(pos.x + size.x - groove_sz().x - dummy_sz().x, pos.y + text_dummy_sz().y); ImVec2 groove_size = is_horizontal ? ImVec2(size.x - 2 * thumb_dummy_sz().x - text_dummy_sz().x, groove_sz().y) : - ImVec2(groove_sz().x, size.y - 2 * text_dummy_sz().y); + ImVec2(groove_sz().x, size.y - 1.6 * text_dummy_sz().y); return ImRect(groove_start, groove_start + groove_size); } @@ -180,6 +180,13 @@ void ImGuiControl::SetSliderValues(const std::vector& values) m_values = values; } +void ImGuiControl::CombineThumbs(bool combine) +{ + m_combine_thumbs = combine; + if (combine) + m_selection = ssHigher; +} + std::string ImGuiControl::get_label(int pos) const { if (m_cb_get_label) @@ -219,9 +226,14 @@ void ImGuiControl::draw_scroll_line(const ImRect& scroll_line, const ImRect& sli ImGui::RenderFrame(scroll_line.Min, scroll_line.Max, thumb_bg_clr, false, m_draw_opts.rounding()); } -void ImGuiControl::draw_background(const ImRect& groove) +void ImGuiControl::draw_background(const ImRect& slideable_region) { - ImVec2 groove_padding = (is_horizontal() ? ImVec2(2.0f, 2.0f) : ImVec2(3.0f, 4.0f)) * m_draw_opts.scale; + ImVec2 groove_sz = m_draw_opts.groove_sz() * 0.55f; + auto groove_center = slideable_region.GetCenter(); + ImRect groove = is_horizontal() ? + ImRect(slideable_region.Min.x, groove_center.y - groove_sz.y, slideable_region.Max.x, groove_center.y + groove_sz.y) : + ImRect(groove_center.x - groove_sz.x, slideable_region.Min.y, groove_center.x + groove_sz.x, slideable_region.Max.y); + ImVec2 groove_padding = (is_horizontal() ? ImVec2(2.0f, 2.0f) : ImVec2(3.0f, 4.0f)) * m_draw_opts.scale; ImRect bg_rect = groove; bg_rect.Expand(groove_padding); @@ -300,7 +312,7 @@ void ImGuiControl::apply_regions(int higher_value, int lower_value, const ImRect ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius); } -void ImGuiControl::check_thumbs(int* higher_value, int* lower_value) +void ImGuiControl::check_and_correct_thumbs(int* higher_value, int* lower_value) { if (!m_draw_lower_thumb || m_combine_thumbs) return; @@ -353,13 +365,13 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, ImGuiContext& context = *GImGui; const ImGuiID id = window->GetID(m_name.c_str()); - const ImRect draw_region(pos, pos + size); - ImGui::ItemSize(draw_region); + const ImRect item_size(pos, pos + size); + ImGui::ItemSize(item_size); - // calc slider groove size + // get slider groove size ImRect groove = m_draw_opts.groove(pos, size, is_horizontal()); - // set active(draggable) region. + // get active(draggable) region. ImRect draggable_region = m_draw_opts.draggable_region(groove, is_horizontal()); if (ImGui::ItemHoverable(draggable_region, id) && context.IO.MouseDown[0]) { @@ -368,9 +380,13 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, ImGui::FocusWindow(window); } - // set slideable region and thumbs. + // set slideable regions and thumbs. apply_regions(*higher_value, *lower_value, draggable_region); + // select and mark higher thumb by default + if (m_selection == ssUndef) + m_selection = ssHigher; + // Processing interacting if (ImGui::ItemHoverable(m_regions.higher_thumb, id) && context.IO.MouseClicked[0]) @@ -382,44 +398,53 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, // update thumb position and value bool value_changed = false; - if (m_selection == ssHigher) + if (m_selection == ssHigher) { value_changed = behavior(id, m_regions.higher_slideable_region, m_min_value, m_max_value, higher_value, &m_regions.higher_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); - else if (m_draw_lower_thumb && !m_combine_thumbs) + } + else if (m_draw_lower_thumb && !m_combine_thumbs) { value_changed = behavior(id, m_regions.lower_slideable_region, m_min_value, m_max_value, lower_value, &m_regions.lower_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); + } + + // check thumbs values and correct them if needed + check_and_correct_thumbs(higher_value, lower_value); + + const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region; + const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb; bool show_move_label = false; - ImRect mouse_pos_rc = m_regions.higher_thumb; - if (!value_changed && ImGui::ItemHoverable(draw_region, id)) { - behavior(id, draggable_region, m_min_value, m_max_value, + ImRect mouse_pos_rc = active_thumb; + if (!value_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) { + behavior(id, slideable_region, m_min_value, m_max_value, &m_mouse_pos_value, &mouse_pos_rc, is_horizontal() ? 0 : ImGuiSliderFlags_Vertical, true); show_move_label = true; } - // check thumbs values and correct them if needed - check_thumbs(higher_value, lower_value); - - // detect right click on thumb - if (ImGui::ItemHoverable(m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb, id) && context.IO.MouseClicked[1]) + // detect right click on selected thumb + if (ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1]) m_rclick_on_selected_thumb = true; - if ((!ImGui::ItemHoverable(m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb, id) && context.IO.MouseClicked[1]) || + if ((!ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[1]) || context.IO.MouseClicked[0]) m_rclick_on_selected_thumb = false; + // render slider + ImVec2 higher_thumb_center = m_regions.higher_thumb.GetCenter(); ImVec2 lower_thumb_center = m_regions.lower_thumb.GetCenter(); - ImRect scroll_line = m_draw_opts.slider_line(draggable_region, higher_thumb_center, lower_thumb_center, is_horizontal()); + ImRect scroll_line = m_draw_opts.slider_line(slideable_region, higher_thumb_center, lower_thumb_center, is_horizontal()); + + if (m_cb_extra_draw) + m_cb_extra_draw(slideable_region); // draw background - draw_background(groove); + draw_background(slideable_region); // draw scroll line - draw_scroll_line(m_combine_thumbs ? groove : scroll_line, groove); + draw_scroll_line(m_combine_thumbs ? groove : scroll_line, slideable_region); // draw thumbs with label - // select and mark higher thumb by default - draw_thumb(higher_thumb_center, m_selection != ssLower && m_draw_lower_thumb); + draw_thumb(higher_thumb_center, m_selection == ssHigher && m_draw_lower_thumb); draw_label(higher_label, m_regions.higher_thumb); if (m_draw_lower_thumb && !m_combine_thumbs) { diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 732ceff57d..406cb51a6e 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -69,21 +69,18 @@ public: void SetMaxValue(const int max_value); void SetSliderValues(const std::vector& values); + void CombineThumbs(bool combine); void SetPos(ImVec2 pos) { m_pos = pos; } void SetSize(ImVec2 size) { m_size = size; } void SetScale(float scale) { m_draw_opts.scale = scale; } void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } - void CombineThumbs(bool combine = true) { m_combine_thumbs = combine; } - - void set_get_label_on_move_cb(std::function cb) { m_cb_get_label_on_move = cb; } - void set_get_label_cb(std::function cb) { m_cb_get_label = cb; } - void set_draw_scroll_line_cb(std::function cb) { m_cb_draw_scroll_line = cb; } bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } bool is_lower_at_min() const { return m_lower_value == m_min_value; } bool is_higher_at_max() const { return m_higher_value == m_max_value; } bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + bool is_rclick_on_thumb() const { return m_rclick_on_selected_thumb; } bool render(SelectedSlider& selection); void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); @@ -91,6 +88,11 @@ public: std::string get_label(int pos) const; std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } + void set_get_label_on_move_cb(std::function cb) { m_cb_get_label_on_move = cb; } + void set_get_label_cb(std::function cb) { m_cb_get_label = cb; } + void set_draw_scroll_line_cb(std::function cb) { m_cb_draw_scroll_line = cb; } + void set_extra_draw_cb(std::function cb) { m_cb_extra_draw = cb; } + struct DrawOptions { float scale { 1.f }; // used for Retina on osx @@ -133,7 +135,6 @@ private: long m_style; double m_label_koef{ 1. }; - bool m_lclick_on_selected_thumb{ false }; bool m_rclick_on_selected_thumb{ false }; bool m_draw_lower_thumb{ true }; @@ -143,13 +144,14 @@ private: std::function m_cb_get_label { nullptr }; std::function m_cb_get_label_on_move { nullptr }; std::function m_cb_draw_scroll_line { nullptr }; + std::function m_cb_extra_draw { nullptr }; void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); - void check_thumbs(int* higher_value, int* lower_value); + void check_and_correct_thumbs(int* higher_value, int* lower_value); - void draw_background(const ImRect& groove); + void draw_background(const ImRect& slideable_region); void draw_label(std::string label, const ImRect& thumb); void draw_thumb(const ImVec2& center, bool mark = false); bool draw_slider(int* higher_value, int* lower_value, diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 455e961c00..b776dd5bbe 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -75,6 +75,15 @@ static const std::map font_icons = { {ImGui::SnapMarker , "snap" }, {ImGui::HorizontalHide , "horizontal_hide" }, {ImGui::HorizontalShow , "horizontal_show" }, + + {ImGui::ErrorTick , "error_tick" }, + {ImGui::ErrorTickHovered , "error_tick_f" }, + {ImGui::PausePrint , "pause_print" }, + {ImGui::PausePrintHovered , "pause_print_f" }, + {ImGui::EditGCode , "edit_gcode" }, + {ImGui::EditGCodeHovered , "edit_gcode_f" }, + {ImGui::RemoveTick , "colorchange_del" }, + {ImGui::RemoveTickHovered , "colorchange_del_f" }, }; static const std::map font_icons_large = { @@ -116,6 +125,17 @@ static const std::map font_icons_large = { {ImGui::SlaViewProcessed , "sla_view_processed" }, }; +static const std::map font_icons_medium = { + {ImGui::Lock , "lock_closed" }, + {ImGui::LockHovered , "lock_closed_f" }, + {ImGui::Unlock , "lock_open" }, + {ImGui::UnlockHovered , "lock_open_f" }, + {ImGui::DSRevert , "undo" }, + {ImGui::DSRevertHovered , "undo_f" }, + {ImGui::DSSettings , "cog" }, + {ImGui::DSSettingsHovered , "cog_f" }, +}; + static const std::map font_icons_extra_large = { {ImGui::ClippyMarker , "notification_clippy" }, }; @@ -470,7 +490,7 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp, tooltip, show_edit_btn); } -bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip) +bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip, bool highlight_on_hover/* = true*/) { const ImGuiIO& io = ImGui::GetIO(); const ImTextureID tex_id = io.Fonts->TexID; @@ -481,9 +501,9 @@ bool ImGuiWrapper::image_button(const wchar_t icon, const std::string& tooltip) const ImVec2 size = { float(rect->Width), float(rect->Height) }; const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); - ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, highlight_on_hover ? 1.0f : 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, highlight_on_hover ? 1.0f : 0.0f }); const bool res = ImGuiPureWrap::image_button(tex_id, size, uv0, uv1); ImGui::PopStyleColor(3); @@ -1125,6 +1145,10 @@ void ImGuiWrapper::init_font(bool compress) m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); } + for (auto& icon : font_icons_medium) { + m_custom_glyph_rects_ids[icon.first] = + io.Fonts->AddCustomRectFontGlyph(font, icon.first, 1.5 * icon_sz, 1.5 * icon_sz, 3.0 * font_scale + 1.5 * icon_sz); + } for (auto& icon : font_icons_large) { m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); @@ -1161,6 +1185,11 @@ void ImGuiWrapper::init_font(bool compress) load_icon_from_svg(icon, icon_sz); } + const int icon_sz_m = int(1.5 * icon_sz); // default size of medium icon is 24 px + for (auto icon : font_icons_medium) { + load_icon_from_svg(icon, icon_sz_m); + } + icon_sz *= 2; // default size of large icon is 32 px for (auto icon : font_icons_large) { load_icon_from_svg(icon, icon_sz); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 56797f7e3a..8cbec8b390 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -86,8 +86,7 @@ public: bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); - bool image_button(const wchar_t icon, const std::string& tooltip = {}); - + bool image_button(const wchar_t icon, const std::string& tooltip = {}, bool highlight_on_hover = true); void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str, Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized); From d06ef37df92748285a82ec489cc443c517194e6e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 20 Mar 2024 13:57:26 +0100 Subject: [PATCH 05/13] ImguiDoubleSlider: WIP: Set scale in respect to Retina displays + request one more frame for redraw, if value was changed with mouse wheel + some code cleaning --- src/slic3r/GUI/DoubleSlider.cpp | 141 ++++++++++++--------------- src/slic3r/GUI/DoubleSlider.hpp | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/GUI/GUI_Preview.cpp | 6 +- src/slic3r/GUI/GUI_Preview.hpp | 2 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 38 ++++---- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 40 ++++---- src/slic3r/GUI/ImGuiWrapper.cpp | 4 +- src/slic3r/GUI/Plater.cpp | 10 +- src/slic3r/GUI/Plater.hpp | 2 +- 10 files changed, 123 insertions(+), 132 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 55e3eafe0e..8d3e55a386 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -51,12 +51,9 @@ namespace DoubleSlider { constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 -static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar -static const float BTN_SZ = 24.f; - -static const ImVec2 ONE_LAYER_OFFSET = ImVec2(41.0f, /*44*/33.0f); -static const ImVec2 HORIZONTAL_SLIDER_SIZE = ImVec2(764.0f, 90.0f);//764 = 680 + handle_dummy_width * 2 + text_right_dummy -static const ImVec2 VERTICAL_SLIDER_SIZE = ImVec2(105.0f, 748.0f);//748 = 680 + text_dummy_height * 2 +static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar +static const float HORIZONTAL_SLIDER_WIDTH = 90.0f; +static const float VERTICAL_SLIDER_HEIGHT = 105.0f; bool equivalent_areas(const double& bottom_area, const double& top_area) { @@ -85,7 +82,6 @@ Control::Control( wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, -// const wxValidator& val, const wxString& name) : wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), m_lower_value(lowerValue), @@ -170,12 +166,13 @@ Control::Control( wxWindow *parent, imgui_ctrl = ImGuiControl( lowerValue, higherValue, minValue, maxValue, ImVec2(0.f, 0.f), ImVec2(0.f, 0.f), - style, into_u8(name), !is_horizontal()); + is_horizontal() ? 0 : ImGuiSliderFlags_Vertical, + into_u8(name), !is_horizontal()); imgui_ctrl.set_get_label_cb([this](int pos) {return into_u8(get_label(pos)); }); if (!is_horizontal()) { - imgui_ctrl.set_get_label_on_move_cb([this](int pos) {return into_u8(get_label(pos, ltEstimatedTime)); }); + imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_extra_style & wxSL_VALUE_LABEL ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); } @@ -605,19 +602,6 @@ using namespace ImGui; // ImGuiDS -float Control::get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect) { - float pos_ratio = (v_max - v_min) != 0 ? ((float)(value - v_min) / (float)(v_max - v_min)) : 0.0f; - float handle_pos; - if (is_horizontal()) { - handle_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio; - } - else { - pos_ratio = 1.0f - pos_ratio; - handle_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio; - } - return handle_pos; -} - void Control::draw_ticks(const ImRect& slideable_region) { //if(m_draw_mode != dmRegular) @@ -639,11 +623,8 @@ void Control::draw_ticks(const ImRect& slideable_region) const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); const ImU32 tick_hovered_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); - auto get_tick_pos = [this, slideable_region](int tick) - { - int v_min = GetMinValue(); - int v_max = GetMaxValue(); - return get_pos_from_value(v_min, v_max, tick, slideable_region); + auto get_tick_pos = [this, slideable_region](int tick) { + return imgui_ctrl.GetPositionFromValue(tick, slideable_region); }; std::set::const_iterator tick_it = m_ticks.ticks.begin(); @@ -744,15 +725,16 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re ImRect main_band = ImRect(blank_rect); main_band.Expand(blank_padding); - auto draw_band = [](const ImU32& clr, const ImRect& band_rc) - { - ImGui::RenderFrame(band_rc.Min, band_rc.Max, clr, false, band_rc.GetWidth() * 0.5); - //cover round corner - ImGui::RenderFrame(ImVec2(band_rc.Min.x, band_rc.Max.y - band_rc.GetWidth() * 0.5), band_rc.Max, clr, false); - }; + auto draw_band = [](const ImU32& clr, const ImRect& band_rc) { + ImGui::RenderFrame(band_rc.Min, band_rc.Max, clr, false, band_rc.GetWidth() * 0.5); + //cover round corner + ImGui::RenderFrame(ImVec2(band_rc.Min.x, band_rc.Max.y - band_rc.GetWidth() * 0.5), band_rc.Max, clr, false); + }; + auto draw_main_band = [&main_band](const ImU32& clr) { ImGui::RenderFrame(main_band.Min, main_band.Max, clr, false, main_band.GetWidth() * 0.5); - }; + }; + //draw main colored band const int default_color_idx = m_mode == MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; std::arrayrgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]); @@ -764,28 +746,20 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re while (tick_it != m_ticks.ticks.end()) { //get position from tick - tick_pos = get_pos_from_value(GetMinValue(), GetMaxValue(), tick_it->tick, slideable_region); + tick_pos = imgui_ctrl.GetPositionFromValue(tick_it->tick, slideable_region); ImRect band_rect = ImRect(ImVec2(main_band.Min.x, std::min(tick_pos, main_band.Min.y)), ImVec2(main_band.Max.x, std::min(tick_pos, main_band.Max.y))); if (main_band.Contains(band_rect)) { - //draw colored band - if (tick_it->type == ToolChange) { - if ((m_mode == SingleExtruder) || (m_mode == MultiAsSingle)) { - const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it); - if (!clr_str.empty()) { - std::arrayrgba = decode_color_to_float_array(clr_str); - ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); - if (tick_it->tick == 0) - draw_main_band(band_clr); - else - draw_band(band_clr, band_rect); - } - } - } - else if (tick_it->type == ColorChange/* && m_mode == SingleExtruder*/) { - const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it); + if ((m_mode == SingleExtruder && tick_it->type == ColorChange) || + (m_mode == MultiAsSingle && (tick_it->type == ToolChange || tick_it->type == ColorChange))) + { + const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : + tick_it->type == ToolChange ? + get_color_for_tool_change_tick(tick_it) : + get_color_for_color_change_tick(tick_it); + if (!clr_str.empty()) { std::arrayrgba = decode_color_to_float_array(clr_str); ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); @@ -900,62 +874,67 @@ bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hover } -bool Control::imgui_render(GUI::GLCanvas3D& canvas) +void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) { - bool result = false; - GUI::Size cnv_size = canvas.get_canvas_size(); + m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); - int canvas_width = cnv_size.get_width(); - int canvas_height = cnv_size.get_height(); + const Size cnv_size = canvas.get_canvas_size(); + const int canvas_width = cnv_size.get_width(); + const int canvas_height = cnv_size.get_height(); - float scale = (float)wxGetApp().em_unit() / 10.0f; - - scale *= m_scale; ImVec2 pos; ImVec2 size; + const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; + if (is_horizontal()) { pos.x = std::max(LEFT_MARGIN, 0.2f * canvas_width); - pos.y = canvas_height - HORIZONTAL_SLIDER_SIZE.y * scale; - size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_SIZE.y * scale); + pos.y = canvas_height - HORIZONTAL_SLIDER_WIDTH * m_scale; + size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_WIDTH * m_scale); imgui_ctrl.ShowLabelOnMouseMove(false); } else { const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; - pos.x = canvas_width - VERTICAL_SLIDER_SIZE.x * scale - tick_icon_side; - pos.y = ONE_LAYER_OFFSET.y; - size = ImVec2(VERTICAL_SLIDER_SIZE.x * scale, canvas_height - 4 * pos.y); + pos.x = canvas_width - VERTICAL_SLIDER_HEIGHT * m_scale - tick_icon_side; + pos.y = 1.f * action_btn_sz; + if (m_allow_editing) + pos.y += 2.f; + size = ImVec2(VERTICAL_SLIDER_HEIGHT * m_scale, canvas_height - 4.f * action_btn_sz); imgui_ctrl.ShowLabelOnMouseMove(true); } - imgui_ctrl.SetPos(pos); - imgui_ctrl.SetSize(size); - imgui_ctrl.SetScale(scale); + imgui_ctrl.Init(pos, size, m_scale); - const float btn_sz = BTN_SZ*scale; - ImVec2 btn_pos = ImVec2(pos.x + 2.7 * btn_sz, pos.y - 0.5 * btn_sz); + if (imgui_ctrl.render(m_selection)) { + // request one more frame if value was changes with mouse wheel + if (GImGui->IO.MouseWheel != 0.0f) + wxGetApp().imgui()->set_requires_extra_frame(); - if (!is_horizontal() && !m_ticks.empty() && m_allow_editing && - render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) - discard_all_thicks(); - - if (imgui_ctrl.render(m_selection)) SetSelectionSpan(m_is_one_layer ? imgui_ctrl.GetHigherValue() : imgui_ctrl.GetLowerValue(), imgui_ctrl.GetHigherValue()); + } + // draw action buttons if (!is_horizontal()) { - btn_pos.y += 0.5 * btn_sz + size.y; + const float groove_center_x = imgui_ctrl.GetGrooveRect().GetCenter().x; + + ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); + + if (!m_ticks.empty() && m_allow_editing && + render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) + discard_all_thicks(); + + btn_pos.y += 0.1f * action_btn_sz + size.y; if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, is_one_layer() ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) switch_one_layer_mode(); - btn_pos.y += btn_sz; + btn_pos.y += 1.2f * action_btn_sz; if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) show_cog_icon_context_menu(); if (m_allow_editing) render_menu(); } - return result; } bool Control::is_wipe_tower_layer(int tick) const @@ -2504,7 +2483,7 @@ void Control::show_cog_icon_context_menu() append_menu_item(&menu, wxID_ANY, _L("Jump to height") + " (Shift+G)", "", [this](wxCommandEvent&) { jump_to_value(); }, "", & menu); - +#if 0 // old code wxMenu* ruler_mode_menu = new wxMenu(); if (ruler_mode_menu) { append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("None"), _L("Hide ruler"), @@ -2522,7 +2501,11 @@ void Control::show_cog_icon_context_menu() append_submenu(&menu, ruler_mode_menu, wxID_ANY, _L("Ruler mode"), _L("Set ruler mode"), "", []() { return true; }, this); } - +#else + append_menu_check_item(&menu, wxID_ANY, _L("Show estimated print time on mouse moving"), _L("Show estimated print time on the ruler"), + [this](wxCommandEvent&) { m_extra_style& wxSL_VALUE_LABEL ? m_extra_style ^= wxSL_VALUE_LABEL : m_extra_style |= wxSL_VALUE_LABEL; }, &menu, + []() { return true; }, [this]() { return m_extra_style & wxSL_VALUE_LABEL; }, GUI::wxGetApp().plater()); +#endif if (m_mode == MultiAsSingle && m_draw_mode == dmRegular) append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "", [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index b021c774f9..e67bd95447 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -222,7 +222,6 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxSL_VERTICAL, -// const wxValidator& val = wxDefaultValidator, const wxString& name = wxEmptyString); ~Control() {} @@ -307,7 +306,7 @@ public: void show_cog_icon_context_menu(); void auto_color_change(); - bool imgui_render(GUI::GLCanvas3D& canvas); + void imgui_render(GUI::GLCanvas3D& canvas, float extra_scale = 1.f); protected: @@ -486,7 +485,6 @@ private: float m_scale{ 1.0 }; bool m_can_change_color{ true }; bool m_show_menu{ false }; - float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f00660708d..28cf4f0d7b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1950,7 +1950,13 @@ void GLCanvas3D::render() wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); - wxGetApp().plater()->render_imgui_double_slider(*this); + wxGetApp().plater()->render_sliders(*this, +#if ENABLE_RETINA_GL + m_retina_helper->get_scale_factor() +#else + 1.f +#endif + ); wxGetApp().imgui()->render(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index de63d7495d..d755272a1b 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -348,12 +348,12 @@ void Preview::sys_color_changed() } -void Preview::render_imgui_double_slider(GLCanvas3D& canvas) +void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) { if (m_layers_slider && m_layers_slider->IsShown()) - m_layers_slider->imgui_render(canvas); + m_layers_slider->imgui_render(canvas, extra_scale); if (m_moves_slider && m_moves_slider->IsShown() && m_bottom_toolbar_panel->IsShown()) - m_moves_slider->imgui_render(canvas); + m_moves_slider->imgui_render(canvas, extra_scale); } void Preview::jump_layers_slider(wxKeyEvent& evt) diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index c720cf771d..1b9da04f30 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -140,7 +140,7 @@ public: void move_layers_slider(wxKeyEvent& evt); void edit_layers_slider(wxKeyEvent& evt); - void render_imgui_double_slider(GLCanvas3D& canvas); + void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index f06ab7eb98..c68097a922 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -17,9 +17,7 @@ static bool behavior(ImGuiID id, const ImRect& region, const ImS32 v_min, const ImS32 v_max, ImS32* out_value, ImRect* out_thumb, ImGuiSliderFlags flags = 0, - bool change_on_mouse_move = false, - const int fixed_value = -1, - const ImVec4& fixed_rect = ImVec4()) + bool change_on_mouse_move = false) { ImGuiContext& context = *GImGui; @@ -63,11 +61,6 @@ static bool behavior(ImGuiID id, const ImRect& region, v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f); } } - // click in fixed_rect behavior - if (ImGui::ItemHoverable(fixed_rect, id) && context.IO.MouseReleased[0]) - { - v_new = fixed_value; - } // apply result, output value if (*out_value != v_new) @@ -88,7 +81,7 @@ static bool behavior(ImGuiID id, const ImRect& region, return value_changed; } -ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) +ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const { ImVec2 groove_start = is_horizontal ? ImVec2(pos.x + thumb_dummy_sz().x, pos.y + size.y - groove_sz().y - dummy_sz().y) : @@ -100,7 +93,7 @@ ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, return ImRect(groove_start, groove_start + groove_size); } -ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is_horizontal) +ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is_horizontal) const { ImRect draggable_region = is_horizontal ? ImRect(groove.Min.x, groove.GetCenter().y, groove.Max.x, groove.GetCenter().y) : @@ -112,7 +105,7 @@ ImRect ImGuiControl::DrawOptions::draggable_region(const ImRect& groove, bool is return draggable_region; } -ImRect ImGuiControl::DrawOptions::slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) +ImRect ImGuiControl::DrawOptions::slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const { ImVec2 mid = draggable_region.GetCenter(); @@ -129,7 +122,7 @@ ImGuiControl::ImGuiControl( int lowerValue, int maxValue, ImVec2 pos, ImVec2 size, - long style, + ImGuiSliderFlags flags, std::string name, bool use_lower_thumb) : m_selection(ssUndef), @@ -140,9 +133,8 @@ ImGuiControl::ImGuiControl( int lowerValue, m_higher_value (higherValue), m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), + m_flags(flags), m_draw_lower_thumb(use_lower_thumb) - //,m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) { } @@ -204,8 +196,11 @@ std::string ImGuiControl::get_label(int pos) const to_string_with_precision(m_values[value]); } -float ImGuiControl::get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect) +float ImGuiControl::GetPositionFromValue(int value, const ImRect& rect) const { + int v_min = m_min_value; + int v_max = m_max_value; + float pos_ratio = (v_max - v_min) != 0 ? ((float)(value - v_min) / (float)(v_max - v_min)) : 0.0f; float thumb_pos; if (is_horizontal()) { @@ -246,6 +241,9 @@ void ImGuiControl::draw_background(const ImRect& slideable_region) void ImGuiControl::draw_label(std::string label, const ImRect& thumb) { + if (label.empty()) + return; + const ImVec2 thumb_center = thumb.GetCenter(); ImVec2 text_padding = m_draw_opts.text_padding(); float rounding = m_draw_opts.rounding(); @@ -301,12 +299,12 @@ void ImGuiControl::apply_regions(int higher_value, int lower_value, const ImRect ImRect(draggable_region.Min + ImVec2(0, thumb_radius), draggable_region.Max); // initialize the thumbs. - float higher_thumb_pos = get_pos_from_value(m_min_value, m_max_value, higher_value, m_regions.higher_slideable_region); + float higher_thumb_pos = GetPositionFromValue(higher_value, m_regions.higher_slideable_region); m_regions.higher_thumb = is_horizontal() ? ImRect(higher_thumb_pos - thumb_radius, mid.y - thumb_radius, higher_thumb_pos + thumb_radius, mid.y + thumb_radius) : ImRect(mid.x - thumb_radius, higher_thumb_pos - thumb_radius, mid.x + thumb_radius, higher_thumb_pos + thumb_radius); - float lower_thumb_pos = get_pos_from_value(m_min_value, m_max_value, lower_value, m_regions.lower_slideable_region); + float lower_thumb_pos = GetPositionFromValue(lower_value, m_regions.lower_slideable_region); m_regions.lower_thumb = is_horizontal() ? ImRect(lower_thumb_pos - thumb_radius, mid.y - thumb_radius, lower_thumb_pos + thumb_radius, mid.y + thumb_radius) : ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius); @@ -400,11 +398,11 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, bool value_changed = false; if (m_selection == ssHigher) { value_changed = behavior(id, m_regions.higher_slideable_region, m_min_value, m_max_value, - higher_value, &m_regions.higher_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); + higher_value, &m_regions.higher_thumb, m_flags); } else if (m_draw_lower_thumb && !m_combine_thumbs) { value_changed = behavior(id, m_regions.lower_slideable_region, m_min_value, m_max_value, - lower_value, &m_regions.lower_thumb, is_horizontal() ? 0: ImGuiSliderFlags_Vertical); + lower_value, &m_regions.lower_thumb, m_flags); } // check thumbs values and correct them if needed @@ -417,7 +415,7 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, ImRect mouse_pos_rc = active_thumb; if (!value_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) { behavior(id, slideable_region, m_min_value, m_max_value, - &m_mouse_pos_value, &mouse_pos_rc, is_horizontal() ? 0 : ImGuiSliderFlags_Vertical, true); + &m_mouse_pos_value, &mouse_pos_rc, m_flags, true); show_move_label = true; } diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 406cb51a6e..5683fc64bd 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -48,7 +48,7 @@ public: int maxValue, ImVec2 pos, ImVec2 size, - long style = wxSL_VERTICAL, + ImGuiSliderFlags flags = ImGuiSliderFlags_None, std::string name = "d_slider", bool use_lower_thumb = true); ImGuiControl() {} @@ -61,6 +61,7 @@ public: int GetLowerValue() const { return m_lower_value; } int GetHigherValue() const { return m_higher_value; } int GetActiveValue() const; + float GetPositionFromValue(int value, const ImRect& rect) const; // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. void SetLowerValue (const int lower_val); @@ -74,9 +75,15 @@ public: void SetPos(ImVec2 pos) { m_pos = pos; } void SetSize(ImVec2 size) { m_size = size; } void SetScale(float scale) { m_draw_opts.scale = scale; } + void Init(const ImVec2& pos, const ImVec2& size, float scale) { + m_pos = pos; + m_size = size; + m_draw_opts.scale = scale; + } void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } + ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); } - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_horizontal() const { return !(m_flags & ImGuiSliderFlags_Vertical); } bool is_lower_at_min() const { return m_lower_value == m_min_value; } bool is_higher_at_max() const { return m_higher_value == m_max_value; } bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } @@ -86,7 +93,6 @@ public: void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); std::string get_label(int pos) const; - std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } void set_get_label_on_move_cb(std::function cb) { m_cb_get_label_on_move = cb; } void set_get_label_cb(std::function cb) { m_cb_get_label = cb; } @@ -96,20 +102,20 @@ public: struct DrawOptions { float scale { 1.f }; // used for Retina on osx - ImVec2 dummy_sz() { return ImVec2(24.0f, 44.0f) * scale; } - ImVec2 thumb_dummy_sz() { return ImVec2(17.0f, 17.0f) * scale; } - ImVec2 groove_sz() { return ImVec2(10.0f, 8.0f) * scale; } - ImVec2 draggable_region_sz() { return ImVec2(40.0f, 19.0f) * scale; } - ImVec2 text_dummy_sz() { return ImVec2(50.0f, 34.0f) * scale; } - ImVec2 text_padding() { return ImVec2( 5.0f, 2.0f) * scale; } + ImVec2 dummy_sz() const { return ImVec2(24.0f, 44.0f) * scale; } + ImVec2 thumb_dummy_sz() const { return ImVec2(17.0f, 17.0f) * scale; } + ImVec2 groove_sz() const { return ImVec2(10.0f, 8.0f) * scale; } + ImVec2 draggable_region_sz()const { return ImVec2(40.0f, 19.0f) * scale; } + ImVec2 text_dummy_sz() const { return ImVec2(50.0f, 34.0f) * scale; } + ImVec2 text_padding() const { return ImVec2( 5.0f, 2.0f) * scale; } - float thumb_radius() { return 14.0f * scale; } - float thumb_border() { return 2.0f * scale; } - float rounding() { return 2.0f * scale; } + float thumb_radius() const { return 14.0f * scale; } + float thumb_border() const { return 2.0f * scale; } + float rounding() const { return 2.0f * scale; } - ImRect groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal); - ImRect draggable_region(const ImRect& groove, bool is_horizontal); - ImRect slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal); + ImRect groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const; + ImRect draggable_region(const ImRect& groove, bool is_horizontal) const; + ImRect slider_line(const ImRect& draggable_region, const ImVec2& h_thumb_center, const ImVec2& l_thumb_center, bool is_horizontal) const; }; struct Regions { @@ -125,6 +131,7 @@ private: ImVec2 m_pos; ImVec2 m_size; std::string m_name; + ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None }; int m_min_value; int m_max_value; @@ -132,7 +139,6 @@ private: int m_higher_value; int m_mouse_pos_value; - long m_style; double m_label_koef{ 1. }; bool m_rclick_on_selected_thumb{ false }; @@ -147,8 +153,8 @@ private: std::function m_cb_extra_draw { nullptr }; void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); + std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } - float get_pos_from_value(int v_min, int v_max, int value, const ImRect& rect); void check_and_correct_thumbs(int* higher_value, int* lower_value); void draw_background(const ImRect& slideable_region); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b776dd5bbe..52a3d84494 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1145,9 +1145,10 @@ void ImGuiWrapper::init_font(bool compress) m_custom_glyph_rects_ids[icon.first] = io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); } + const int icon_sz_m = int(1.25 * icon_sz); // default size of medium icon is 20 px for (auto& icon : font_icons_medium) { m_custom_glyph_rects_ids[icon.first] = - io.Fonts->AddCustomRectFontGlyph(font, icon.first, 1.5 * icon_sz, 1.5 * icon_sz, 3.0 * font_scale + 1.5 * icon_sz); + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz_m, icon_sz_m, 3.0 * font_scale + icon_sz_m); } for (auto& icon : font_icons_large) { m_custom_glyph_rects_ids[icon.first] = @@ -1185,7 +1186,6 @@ void ImGuiWrapper::init_font(bool compress) load_icon_from_svg(icon, icon_sz); } - const int icon_sz_m = int(1.5 * icon_sz); // default size of medium icon is 24 px for (auto icon : font_icons_medium) { load_icon_from_svg(icon, icon_sz_m); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1db3fac3f4..b32d44e55b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -385,7 +385,7 @@ struct Plater::priv void set_current_canvas_as_dirty(); GLCanvas3D* get_current_canvas3D(); - void render_imgui_double_slider(GLCanvas3D& canvas); + void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); void unbind_canvas_event_handlers(); void reset_canvas_volumes(); @@ -3190,10 +3190,10 @@ GLCanvas3D* Plater::priv::get_current_canvas3D() return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); } -void Plater::priv::render_imgui_double_slider(GLCanvas3D& canvas) +void Plater::priv::render_sliders(GLCanvas3D& canvas, float extra_scale /*= 0.1f*/) { if (current_panel == preview) - preview->render_imgui_double_slider(canvas); + preview->render_sliders(canvas, extra_scale); } void Plater::priv::unbind_canvas_event_handlers() @@ -6409,9 +6409,9 @@ GLCanvas3D* Plater::get_current_canvas3D() return p->get_current_canvas3D(); } -void Plater::render_imgui_double_slider(GLCanvas3D& canvas) +void Plater::render_sliders(GLCanvas3D& canvas, float extra_scale) { - p->render_imgui_double_slider(canvas); + p->render_sliders(canvas, extra_scale); } static std::string concat_strings(const std::set &strings, diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 97c6c18772..656ef4a2f7 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -272,7 +272,7 @@ public: const GLCanvas3D * canvas3D() const; GLCanvas3D* get_current_canvas3D(); - void render_imgui_double_slider(GLCanvas3D& canvas); + void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); void arrange(); void arrange(Worker &w, bool selected); From 6ea3dc6c27b85037ed9c5205a6d64e48b32fda3d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 21 Mar 2024 22:00:12 +0100 Subject: [PATCH 06/13] ImguiDoubleSlider: WIP: All data, related to Slider are in ImGuiControl now + code cleaning to avoid of use wxWidgets to render DoubleSlider --- src/slic3r/GUI/DoubleSlider.cpp | 1599 ++------------------------ src/slic3r/GUI/DoubleSlider.hpp | 193 +--- src/slic3r/GUI/GUI_Preview.cpp | 76 +- src/slic3r/GUI/GUI_Preview.hpp | 5 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 79 +- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 30 +- src/slic3r/GUI/Plater.cpp | 1 - 7 files changed, 212 insertions(+), 1771 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 8d3e55a386..f1f2b5e31d 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -74,211 +74,60 @@ static std::string gcode(Type type) } Control::Control( wxWindow *parent, - wxWindowID id, int lowerValue, int higherValue, int minValue, int maxValue, - const wxPoint& pos, - const wxSize& size, long style, const wxString& name) : - wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), - m_higher_value (higherValue), - m_min_value(minValue), - m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL), + wxControl(parent, wxID_ANY, wxDefaultPosition, wxSize(0,0)), m_allow_editing(wxGetApp().is_editor()), - m_extra_style(style == wxSL_VERTICAL ? wxSL_AUTOTICKS | wxSL_VALUE_LABEL : 0) + m_show_estimated_times(style == wxSL_VERTICAL) { #ifdef __WXOSX__ is_osx = true; #endif //__WXOSX__ - if (!is_osx) - SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up")); - m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.GetSize(); - - m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); - m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); - m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); - m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); - - m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); - m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); - m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); - m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); - - m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = m_bmp_revert.GetWidth(); - m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = m_bmp_cog.GetWidth(); - - m_selection = ssUndef; m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); m_ticks.set_extruder_colors(&m_extruder_colors); - // slider events - this->Bind(wxEVT_PAINT, &Control::OnPaint, this); - this->Bind(wxEVT_CHAR, &Control::OnChar, this); - this->Bind(wxEVT_LEFT_DOWN, &Control::OnLeftDown, this); - this->Bind(wxEVT_MOTION, &Control::OnMotion, this); - this->Bind(wxEVT_LEFT_UP, &Control::OnLeftUp, this); - this->Bind(wxEVT_MOUSEWHEEL, &Control::OnWheel, this); - this->Bind(wxEVT_ENTER_WINDOW,&Control::OnEnterWin, this); - this->Bind(wxEVT_LEAVE_WINDOW,&Control::OnLeaveWin, this); - this->Bind(wxEVT_KEY_DOWN, &Control::OnKeyDown, this); - this->Bind(wxEVT_KEY_UP, &Control::OnKeyUp, this); - this->Bind(wxEVT_RIGHT_DOWN, &Control::OnRightDown,this); - this->Bind(wxEVT_RIGHT_UP, &Control::OnRightUp, this); - this->Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { - m_ruler.update(m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); - event.Skip(); - }); - - // control's view variables - SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); - - DARK_ORANGE_PEN = wxPen(wxColour(237, 107, 33)); - ORANGE_PEN = wxPen(wxColour(253, 126, 66)); - LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); - - DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); - GREY_PEN = wxPen(wxColour(164, 164, 164)); - LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); - - m_line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; - m_segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; - - FOCUS_RECT_PEN = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - FOCUS_RECT_BRUSH = wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT); - - m_font = GetFont(); - this->SetMinSize(get_min_size()); - - if (style == wxSL_VERTICAL) - m_ruler.set_parent(this->GetParent()); imgui_ctrl = ImGuiControl( lowerValue, higherValue, minValue, maxValue, ImVec2(0.f, 0.f), ImVec2(0.f, 0.f), - is_horizontal() ? 0 : ImGuiSliderFlags_Vertical, - into_u8(name), !is_horizontal()); + style == wxSL_VERTICAL ? ImGuiSliderFlags_Vertical : 0, + into_u8(name), style == wxSL_VERTICAL); imgui_ctrl.set_get_label_cb([this](int pos) {return into_u8(get_label(pos)); }); - if (!is_horizontal()) { - imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_extra_style & wxSL_VALUE_LABEL ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); + if (!imgui_ctrl.is_horizontal()) { + imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); } } -void Control::msw_rescale() -{ - m_font = GUI::wxGetApp().normal_font(); - - m_thumb_size = m_bmp_thumb_lower.GetSize(); - m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); - m_revert_icon_dim = m_bmp_revert.GetWidth(); - m_cog_icon_dim = m_bmp_cog.GetWidth(); - - SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); - - SetMinSize(get_min_size()); - GetParent()->Layout(); - - m_ruler.update_dpi(); - m_ruler.update(m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); -} - -void Control::sys_color_changed() -{ - GUI::wxGetApp().UpdateDarkUI(GetParent()); - - m_bmp_add_tick_on .sys_color_changed(); - m_bmp_add_tick_off.sys_color_changed(); - m_bmp_del_tick_on .sys_color_changed(); - m_bmp_del_tick_off.sys_color_changed(); - - m_bmp_one_layer_lock_on .sys_color_changed(); - m_bmp_one_layer_lock_off .sys_color_changed(); - m_bmp_one_layer_unlock_on .sys_color_changed(); - m_bmp_one_layer_unlock_off.sys_color_changed(); - - m_bmp_revert.sys_color_changed(); - m_bmp_cog .sys_color_changed(); -} - -int Control::GetActiveValue() const -{ - return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; -} - -wxSize Control::get_min_size() const -{ - const int min_side = GUI::wxGetApp().em_unit() * ( is_horizontal() ? 5 : 11 ); - return wxSize(min_side, min_side); -} - -wxSize Control::DoGetBestSize() const -{ - const wxSize size = wxControl::DoGetBestSize(); - if (size.x > 1 && size.y > 1) - return size; - return get_min_size(); -} - void Control::SetLowerValue(const int lower_val) { - m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); - Refresh(); - Update(); + imgui_ctrl.SetLowerValue(lower_val); wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); - - imgui_ctrl.SetLowerValue(lower_val); } void Control::SetHigherValue(const int higher_val) { - m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); - Refresh(); - Update(); + imgui_ctrl.SetHigherValue(higher_val); wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); - - imgui_ctrl.SetHigherValue(higher_val); } void Control::SetSelectionSpan(const int lower_val, const int higher_val) { - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) - m_is_one_layer = false; - - imgui_ctrl.CombineThumbs(m_is_one_layer); imgui_ctrl.SetSelectionSpan(lower_val, higher_val); - Refresh(); - Update(); - wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); @@ -286,92 +135,23 @@ void Control::SetSelectionSpan(const int lower_val, const int higher_val) void Control::SetMaxValue(const int max_value) { - m_max_value = max_value; - Refresh(); - Update(); - imgui_ctrl.SetMaxValue(max_value); } void Control::SetSliderValues(const std::vector& values) { m_values = values; - m_ruler.init(m_values, get_scroll_step()); - - // When "No sparce layer" is enabled, use m_layers_values for ruler update. - // Because of m_values has duplicate values in this case. -// m_ruler.update(this->GetParent(), m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); - - imgui_ctrl.SetSliderValues(values); -} - -void Control::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) -{ - int width; - int height; - get_size(&width, &height); - - wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; - wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; - wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; - wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; - - wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; - wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/; - wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; - wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - - for (size_t id = 0; id < m_line_pens.size(); id++) { - dc.SetPen(*m_line_pens[id]); - dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); - dc.SetPen(*m_segm_pens[id]); - dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); - if (is_horizontal()) - line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; - else - line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; - } -} - -double Control::get_scroll_step() -{ - const wxSize sz = get_size(); - const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); -} - -// get position on the slider line from entered value -wxCoord Control::get_position_from_value(const int value) -{ - const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; - return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); -} - -wxSize Control::get_size() const -{ - int w, h; - get_size(&w, &h); - return wxSize(w, h); -} - -void Control::get_size(int* w, int* h) const -{ - GetSize(w, h); - if (m_draw_mode == dmSequentialGCodeView) - return; // we have no more icons for drawing - is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; } double Control::get_double_value(const SelectedSlider& selection) { - if (m_values.empty() || m_lower_value<0) + if (m_values.empty() || imgui_ctrl.GetLowerValue() < 0) return 0.0; - if (m_values.size() <= size_t(m_higher_value)) { - correct_higher_value(); + if (m_values.size() <= size_t(imgui_ctrl.GetHigherValue())) { + imgui_ctrl.correct_higher_value(); return m_values.back(); } - return m_values[selection == ssLower ? m_lower_value : m_higher_value]; + return m_values[selection == ssLower ? imgui_ctrl.GetLowerValue() : imgui_ctrl.GetHigherValue()]; } int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) @@ -429,18 +209,12 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) post_ticks_changed_event(); // init extruder sequence in respect to the extruders count - if (m_ticks.empty()) { + if (m_ticks.empty()) m_extruders_sequence.init(m_extruder_colors.size()); - imgui_ctrl.set_draw_scroll_line_cb(nullptr); - } - else - imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); + update_callbacks(); if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; - - Refresh(); - Update(); } void Control::SetLayersTimes(const std::vector& layers_times, float total_time) @@ -466,12 +240,7 @@ void Control::SetLayersTimes(const std::vector& layers_times, float total if (m_layers_values.size() != m_layers_times.size()) for (size_t i = m_layers_times.size(); i < m_layers_values.size(); i++) m_layers_times.push_back(total_time); - m_ruler.update(m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); - Refresh(); - Update(); } - else - m_ruler.update(m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); } void Control::SetLayersTimes(const std::vector& layers_times) @@ -487,6 +256,7 @@ void Control::SetDrawMode(bool is_sla_print, bool is_sequential_print) m_draw_mode = is_sla_print ? dmSlaPrint : is_sequential_print ? dmSequentialFffPrint : dmRegular; + update_callbacks(); } void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) @@ -498,7 +268,8 @@ void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, c m_ticks.mode = m_mode; m_only_extruder = only_extruder; - UseDefaultColors(m_mode == SingleExtruder); + if (m_mode != SingleExtruder) + UseDefaultColors(false); m_is_wipe_tower = m_mode != SingleExtruder; } @@ -524,84 +295,16 @@ bool Control::IsNewPrint() return true; } -void Control::get_lower_and_higher_position(int& lower_pos, int& higher_pos) +void Control::update_callbacks() { - const double step = get_scroll_step(); - if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); - } - else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); - } -} - -void Control::draw_focus_rect(wxDC& dc) -{ - if (!m_is_focused) - return; - const wxSize sz = GetSize(); -// wxPaintDC dc(this); - //const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - //dc.SetPen(pen); - //dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); - dc.SetPen(FOCUS_RECT_PEN); - dc.SetBrush(FOCUS_RECT_BRUSH); - dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); -} - -void Control::render() -{ -#ifdef _WIN32 - GUI::wxGetApp().UpdateDarkUI(this); -#else - SetBackgroundColour(GetParent()->GetBackgroundColour()); -#endif // _WIN32 - - wxPaintDC dc(this); - dc.SetFont(m_font); - - draw_focus_rect(dc); - - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); - - // draw colored band on the background of a scroll line - // and only in a case of no-empty m_values - draw_colored_band(dc); - - if (m_extra_style & wxSL_AUTOTICKS) - draw_ruler(dc); - - if (!m_render_as_disabled) { - // draw line - draw_scroll_line(dc, lower_pos, higher_pos); - - // draw color print ticks - draw_ticks(dc); - - // draw both sliders - draw_thumbs(dc, lower_pos, higher_pos); - - // draw lock/unlock - draw_one_layer_icon(dc); - - // draw revert bitmap (if it's shown) - draw_revert_icon(dc); - - // draw cog bitmap (if it's shown) - draw_cog_icon(dc); - - // draw mouse position - draw_tick_on_mouse_position(dc); - } + if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) + imgui_ctrl.set_draw_scroll_line_cb(nullptr); + else + imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); } using namespace ImGui; -// ImGuiDS - void Control::draw_ticks(const ImRect& slideable_region) { //if(m_draw_mode != dmRegular) @@ -643,9 +346,7 @@ void Control::draw_ticks(const ImRect& slideable_region) ++tick_it; } - auto active_tick_it = m_selection == ssHigher ? m_ticks.ticks.find(TickCode{ this->GetHigherValue() }) : - m_selection == ssLower ? m_ticks.ticks.find(TickCode{ this->GetLowerValue() }) : - m_ticks.ticks.end(); + auto active_tick_it = m_ticks.ticks.find(TickCode{ imgui_ctrl.GetActiveValue() }); tick_it = m_ticks.ticks.begin(); while (tick_it != m_ticks.ticks.end()) @@ -665,7 +366,7 @@ void Control::draw_ticks(const ImRect& slideable_region) bool activate_this_tick = false; if (tick_it == active_tick_it && m_allow_editing) { // delete tick - if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, m_selection == ssHigher ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { + if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, imgui_ctrl.IsActiveHigherThumb() ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { Type type = tick_it->type; m_ticks.ticks.erase(tick_it); post_ticks_changed_event(type); @@ -684,7 +385,7 @@ void Control::draw_ticks(const ImRect& slideable_region) activate_this_tick = render_button(ImGui::EditGCode, ImGui::EditGCodeHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); if (activate_this_tick) { - m_selection == ssHigher ? SetHigherValue(tick_it->tick) : SetLowerValue(tick_it->tick); + imgui_ctrl.IsActiveHigherThumb() ? SetHigherValue(tick_it->tick) : SetLowerValue(tick_it->tick); break; } @@ -713,8 +414,9 @@ static std::array decode_color_to_float_array(const std::string color) return ret; } -void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) { - if (m_ticks.empty()) +void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) +{ + if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) return; ImVec2 blank_padding = ImVec2(5.0f, 2.0f) * m_scale; @@ -791,14 +493,14 @@ void Control::render_menu() ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); if (ImGui::BeginPopup("slider_menu_popup")) { - if ((m_selection == ssLower && GetLowerValueD() == 0.0) || (m_selection == ssHigher && GetHigherValueD() == 0.0)) + if ((!imgui_ctrl.IsActiveHigherThumb() && GetLowerValueD() == 0.0) || + (imgui_ctrl.IsActiveHigherThumb() && GetHigherValueD() == 0.0)) { menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, false); } else { if (menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) { - UseDefaultColors(false); add_code_as_tick(ColorChange); } if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { @@ -861,6 +563,7 @@ bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hover ImGuiContext& g = *GImGui; + m_focus = focus; std::string tooltip = m_allow_editing ? into_u8(get_tooltip(tick)) : ""; ImGui::SetCursorPos(ImVec2(0, 0)); const bool ret = m_imgui->image_button(g.HoveredWindow == g.CurrentWindow ? btn_icon_hovered : btn_icon, tooltip, false); @@ -873,9 +576,10 @@ bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hover return ret; } - void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) { + if (!imgui_ctrl.IsShown()) + return; m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); const Size cnv_size = canvas.get_canvas_size(); @@ -887,7 +591,7 @@ void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/ const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; - if (is_horizontal()) { + if (imgui_ctrl.is_horizontal()) { pos.x = std::max(LEFT_MARGIN, 0.2f * canvas_width); pos.y = canvas_height - HORIZONTAL_SLIDER_WIDTH * m_scale; size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_WIDTH * m_scale); @@ -905,17 +609,24 @@ void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/ } imgui_ctrl.Init(pos, size, m_scale); + if (imgui_ctrl.render()) { + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); + } - if (imgui_ctrl.render(m_selection)) { + if (imgui_ctrl.render()) { // request one more frame if value was changes with mouse wheel if (GImGui->IO.MouseWheel != 0.0f) wxGetApp().imgui()->set_requires_extra_frame(); - SetSelectionSpan(m_is_one_layer ? imgui_ctrl.GetHigherValue() : imgui_ctrl.GetLowerValue(), imgui_ctrl.GetHigherValue()); + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); } // draw action buttons - if (!is_horizontal()) { + if (!imgui_ctrl.is_horizontal()) { const float groove_center_x = imgui_ctrl.GetGrooveRect().GetCenter().x; ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); @@ -925,14 +636,20 @@ void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/ discard_all_thicks(); btn_pos.y += 0.1f * action_btn_sz + size.y; - if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, is_one_layer() ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) - switch_one_layer_mode(); + const bool is_one_layer = imgui_ctrl.IsCombineThumbs(); + if (render_button(is_one_layer ? ImGui::Lock : ImGui::Unlock, is_one_layer ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) + imgui_ctrl.CombineThumbs(!is_one_layer); btn_pos.y += 1.2f * action_btn_sz; if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) show_cog_icon_context_menu(); - if (m_allow_editing) + if (m_draw_mode == dmSequentialFffPrint && imgui_ctrl.is_rclick_on_thumb()) { + std::string tooltip = _u8L("The sequential print is on.\n" + "It's impossible to apply any custom G-code for objects printing sequentually."); + ImGuiPureWrap::tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + } + else if (m_allow_editing) render_menu(); } } @@ -950,128 +667,6 @@ bool Control::is_wipe_tower_layer(int tick) const return false; } -void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - if (!m_enable_action_icon) - return; - - // suppress add tick on first layer - if (tick == 0) - return; - - if (is_wipe_tower_layer(tick)) { - m_rect_tick_action = wxRect(); - return; - } - - //wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - //if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) - // icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); - ScalableBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off : &m_bmp_add_tick_on; - if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) - icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off : &m_bmp_del_tick_on; - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; - if (m_selection == ssLower) - is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; - else - is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - - if (m_draw_mode == dmSequentialFffPrint) { - wxBitmap disabled_add = get_bmp_bundle("colorchange_add")->GetBitmapFor(this).ConvertToDisabled(); - dc.DrawBitmap(disabled_add, x_draw, y_draw); - } - else - dc.DrawBitmap((*icon).get_bitmap(), x_draw, y_draw); - - //update rect of the tick action icon - m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); -} - -void Control::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) -{ - if (m_selection == selection) { - //draw info line - dc.SetPen(DARK_ORANGE_PEN); - const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/); - const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/); - dc.DrawLine(pt_beg, pt_end); - - //draw action icon - if (m_draw_mode == dmRegular || m_draw_mode == dmSequentialFffPrint) - draw_action_icon(dc, pt_beg, pt_end); - } -} - -void Control::draw_tick_on_mouse_position(wxDC& dc) -{ - if (!m_is_focused || m_moving_pos == wxDefaultPosition) - return; - - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - - int tick = get_tick_near_point(m_moving_pos); - if (tick == m_higher_value || tick == m_lower_value) - return ; - - auto draw_ticks = [this](wxDC& dc, wxPoint pos, int margin=0 ) - { - wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x+margin, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x , pos.y+margin); - wxPoint pt_end = is_horizontal() ? wxPoint(pos.x+margin, pos.y + m_thumb_size.y) : wxPoint(pos.x - 0.5 * m_thumb_size.x + 1, pos.y+margin); - dc.DrawLine(pt_beg, pt_end); - - pt_beg = is_horizontal() ? wxPoint(pos.x + margin, pos.y - m_thumb_size.y) : wxPoint(pos.x + 0.5 * m_thumb_size.x, pos.y+margin); - pt_end = is_horizontal() ? wxPoint(pos.x + margin, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x + 1, pos.y+margin); - dc.DrawLine(pt_beg, pt_end); - }; - - auto draw_touch = [this](wxDC& dc, wxPoint pos, int margin, bool right_side ) - { - int mult = right_side ? 1 : -1; - wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x - margin, pos.y + mult * m_thumb_size.y) : wxPoint(pos.x + mult * m_thumb_size.x, pos.y - margin); - wxPoint pt_end = is_horizontal() ? wxPoint(pos.x + margin, pos.y + mult * m_thumb_size.y) : wxPoint(pos.x + mult * m_thumb_size.x, pos.y + margin); - dc.DrawLine(pt_beg, pt_end); - }; - - if (tick > 0) // this tick exists and should be marked as a focused - { - wxCoord new_pos = get_position_from_value(tick); - const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); - - dc.SetPen(DARK_ORANGE_PEN); - - draw_ticks(dc, pos, -2); - draw_ticks(dc, pos, 2 ); - draw_touch(dc, pos, 2, true); - draw_touch(dc, pos, 2, false); - - return; - } - - tick = get_value_from_position(m_moving_pos); - if (tick > m_max_value || tick < m_min_value || tick == m_higher_value || tick == m_lower_value) - return; - - wxCoord new_pos = get_position_from_value(tick); - const wxPoint pos = is_horizontal() ? wxPoint(new_pos, height * 0.5) : wxPoint(0.5 * width, new_pos); - - //draw info line - dc.SetPen(LIGHT_GREY_PEN); - draw_ticks(dc, pos); - - if (m_extra_style & wxSL_VALUE_LABEL) { - wxColour old_clr = dc.GetTextForeground(); - dc.SetTextForeground(GREY_PEN.GetColour()); - draw_tick_text(dc, pos, tick, ltEstimatedTime, false); - dc.SetTextForeground(old_clr); - } -} - static wxString short_and_splitted_time(const std::string& time) { // Parse the dhms time format. @@ -1117,7 +712,7 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer { const size_t value = tick; - if (m_label_koef == 1.0 && m_values.empty()) + if (m_values.empty()) return wxString::Format("%lu", static_cast(value)); if (value >= m_values.size()) return "ErrVal"; @@ -1153,9 +748,7 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer } return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; } - wxString str = m_values.empty() ? - wxString::Format("%.*f", 2, m_label_koef * value) : - wxString::Format("%.*f", 2, m_values[value]); + wxString str = wxString::Format("%.*f", 2, m_values[value]); if (label_type == ltHeight) return str; if (label_type == ltHeightWithLayer) { @@ -1167,161 +760,6 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer return wxEmptyString; } -void Control::draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type/* = ltHeight*/, bool right_side/*=true*/) const -{ - wxCoord text_width, text_height; - const wxString label = get_label(tick, label_type); - dc.GetMultiLineTextExtent(label, &text_width, &text_height); - wxPoint text_pos; - if (right_side) { - if (is_horizontal()) { - int width; - int height; - get_size(&width, &height); - - int x_right = pos.x + 1 + text_width; - int xx = (x_right < width) ? pos.x + 1 : pos.x - text_width - 1; - text_pos = wxPoint(xx, pos.y + m_thumb_size.x / 2 + 1); - } - else - text_pos = wxPoint(pos.x + m_thumb_size.x + 1, pos.y - 0.5 * text_height - 1); - } - else { - if (is_horizontal()) { - int x = pos.x - text_width - 1; - int xx = (x > 0) ? x : pos.x + 1; - text_pos = wxPoint(xx, pos.y - m_thumb_size.x / 2 - text_height - 1); - } - else - text_pos = wxPoint(std::max(2, pos.x - text_width - 1 - m_thumb_size.x), pos.y - 0.5 * text_height + 1); - } - - wxColour old_clr = dc.GetTextForeground(); - const wxPen& pen = is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? DARK_ORANGE_PEN : /*wxPen(old_clr)*/GREY_PEN; - dc.SetPen(pen); - dc.SetTextForeground(pen.GetColour()); - - if (label_type == ltEstimatedTime) - dc.DrawLabel(label, wxRect(text_pos, wxSize(text_width, text_height)), wxALIGN_RIGHT); - else - dc.DrawText(label, text_pos); - - dc.SetTextForeground(old_clr); -} - -void Control::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const -{ - draw_tick_text(dc, pos, selection == ssLower ? m_lower_value : m_higher_value, ltHeightWithLayer, selection == ssLower); -} - -void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) -{ - wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x); - wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y); - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.get_bitmap() : m_bmp_thumb_higher.get_bitmap(), x_draw, y_draw); - - // Update thumb rect - update_thumb_rect(x_draw, y_draw, selection); -} - -void Control::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); - - // Draw thumb - draw_thumb_item(dc, pos, selection); - - // Draw info_line - draw_info_line_with_icon(dc, pos, selection); - - // Draw thumb text - draw_thumb_text(dc, pos, selection); -} - -void Control::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); - const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); - - // Draw lower thumb - draw_thumb_item(dc, pos_l, ssLower); - // Draw lower info_line - draw_info_line_with_icon(dc, pos_l, ssLower); - - // Draw higher thumb - draw_thumb_item(dc, pos_h, ssHigher); - // Draw higher info_line - draw_info_line_with_icon(dc, pos_h, ssHigher); - // Draw higher thumb text - draw_thumb_text(dc, pos_h, ssHigher); - - // Draw lower thumb text - draw_thumb_text(dc, pos_l, ssLower); -} - -void Control::draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len) -{ - int mid_space = 9; - is_horizontal() ? dc.DrawLine(pos, mid - (mid_space + tick_len), pos, mid - mid_space) : - dc.DrawLine(mid - (mid_space + tick_len), pos, mid - mid_space, pos); - is_horizontal() ? dc.DrawLine(pos, mid + (mid_space + tick_len), pos, mid + mid_space) : - dc.DrawLine(mid + (mid_space + tick_len), pos, mid + mid_space, pos); -} - -void Control::draw_ticks(wxDC& dc) -{ - if (m_draw_mode == dmSlaPrint) - return; - - dc.SetPen(m_draw_mode == dmRegular ? DARK_GREY_PEN : LIGHT_GREY_PEN ); - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (const TickCode& tick : m_ticks.ticks) { - if (size_t(tick.tick) >= m_values.size()) { - // The case when OnPaint is called before m_ticks.ticks data are updated (specific for the vase mode) - break; - } - - const wxCoord pos = get_position_from_value(tick.tick); - draw_ticks_pair(dc, pos, mid, 7); - - // if current tick if focused, we should to use a specific "focused" icon - bool focused_tick = m_moving_pos != wxDefaultPosition && tick.tick == get_tick_near_point(m_moving_pos); - - // get icon name if it is - std::string icon_name; - - // if we have non-regular draw mode, all ticks should be marked with error icon - if (m_draw_mode != dmRegular) - icon_name = focused_tick ? "error_tick_f" : "error_tick"; - else if (tick.type == ColorChange || tick.type == ToolChange) { - if (m_ticks.is_conflict_tick(tick, m_mode, m_only_extruder, m_values[tick.tick])) - icon_name = focused_tick ? "error_tick_f" : "error_tick"; - } - else if (tick.type == PausePrint) - icon_name = focused_tick ? "pause_print_f" : "pause_print"; - else - icon_name = focused_tick ? "edit_gcode_f" : "edit_gcode"; - - // Draw icon for "Pause print", "Custom Gcode" or conflict tick - if (!icon_name.empty()) { - wxBitmapBundle* icon = get_bmp_bundle(icon_name); - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; - is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; - - dc.DrawBitmap(icon->GetBitmapFor(this), x_draw, y_draw); - } - } -} - std::string Control::get_color_for_tool_change_tick(std::set::const_iterator it) const { const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; @@ -1358,362 +796,6 @@ std::string Control::get_color_for_color_change_tick(std::set::const_i return ""; } -wxRect Control::get_colored_band_rect() -{ - int height, width; - get_size(&width, &height); - - const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - - return is_horizontal() ? - wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), - width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : - wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, - lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); -} - -void Control::draw_colored_band(wxDC& dc) -{ - if (m_draw_mode != dmRegular) - return; - - auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) - { - dc.SetPen(clr); - dc.SetBrush(clr); - dc.DrawRectangle(band_rc); - }; - - wxRect main_band = get_colored_band_rect(); - - // don't color a band for MultiExtruder mode - if (m_ticks.empty() || m_mode == MultiExtruder) { - draw_band(dc, GetParent()->GetBackgroundColour(), main_band); - return; - } - - const int default_color_idx = m_mode==MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; - draw_band(dc, wxColour(m_extruder_colors[default_color_idx]), main_band); - - std::set::const_iterator tick_it = m_ticks.ticks.begin(); - - while (tick_it != m_ticks.ticks.end()) - { - if ( (m_mode == SingleExtruder && tick_it->type == ColorChange ) || - (m_mode == MultiAsSingle && (tick_it->type == ToolChange || tick_it->type == ColorChange)) ) - { - const wxCoord pos = get_position_from_value(tick_it->tick); - is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : - main_band.SetBottom(pos - 1); - - const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : - tick_it->type == ToolChange ? - get_color_for_tool_change_tick(tick_it) : - get_color_for_color_change_tick(tick_it); - - if (!clr_str.empty()) - draw_band(dc, wxColour(clr_str), main_band); - } - ++tick_it; - } -} - -void Control::Ruler::init(const std::vector& values, double scroll_step) -{ - if (!m_parent) - return; - max_values.clear(); - max_values.reserve(std::count(values.begin(), values.end(), values.front())); - - auto it = std::find(values.begin() + 1, values.end(), values.front()); - while (it != values.end()) { - max_values.push_back(*(it - 1)); - it = std::find(it + 1, values.end(), values.front()); - } - max_values.push_back(*(it - 1)); - - update(values, scroll_step); -} - -void Control::Ruler::set_parent(wxWindow* parent) -{ - m_parent = parent; - update_dpi(); -} - -void Control::Ruler::update_dpi() -{ - if (m_parent) - m_DPI = GUI::get_dpi_for_window(m_parent); -} - -void Control::Ruler::update(const std::vector& values, double scroll_step) -{ - if (!m_parent || values.empty() || - // check if need to update ruler in respect to input values - (values.front() == m_min_val && values.back() == m_max_val && m_scroll_step == scroll_step && max_values.size() == m_max_values_cnt)) - return; - - m_min_val = values.front(); - m_max_val = values.back(); - m_scroll_step = scroll_step; - m_max_values_cnt = max_values.size(); - - int pixels_per_sm = lround((double)(m_DPI) * 5.0/25.4); - - if (lround(scroll_step) > pixels_per_sm) { - long_step = -1.0; - return; - } - - int pow = -2; - int step = 0; - auto end_it = std::find(values.begin() + 1, values.end(), values.front()); - - while (pow < 3) { - for (int istep : {1, 2, 5}) { - double val = (double)istep * std::pow(10,pow); - auto val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); - - if (val_it == values.end()) - break; - int tick = val_it - values.begin(); - - // find next tick with istep - val *= 2; - val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); - // count of short ticks between ticks - int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick; - - if (lround(short_ticks_cnt * scroll_step) > pixels_per_sm) { - step = istep; - // there couldn't be more then 10 short ticks between ticks - short_step = 0.1 * short_ticks_cnt; - break; - } - } - if (step > 0) - break; - pow++; - } - - long_step = step == 0 ? -1.0 : (double)step* std::pow(10, pow); - if (long_step < 0) - short_step = long_step; -} - -void Control::draw_ruler(wxDC& dc) -{ - if (m_values.empty() || !m_ruler.can_draw()) - return; - // When "No sparce layer" is enabled, use m_layers_values for ruler update. - // Because of m_values has duplicate values in this case. -// m_ruler.update(this->GetParent(), m_layers_values.empty() ? m_values : m_layers_values, get_scroll_step()); - - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - - dc.SetPen(GREY_PEN); - wxColour old_clr = dc.GetTextForeground(); - dc.SetTextForeground(GREY_PEN.GetColour()); - - auto draw_short_ticks = [this, mid](wxDC& dc, double& current_tick, int max_tick) { - if (m_ruler.short_step <= 0.0) - return; - while (current_tick < max_tick) { - wxCoord pos = get_position_from_value(lround(current_tick)); - draw_ticks_pair(dc, pos, mid, 2); - current_tick += m_ruler.short_step; - if (current_tick > m_max_value) - break; - } - }; - - double short_tick = NaNd; - int tick = 0; - double value = 0.0; - size_t sequence = 0; - int prev_y_pos = -1; - wxCoord label_height = dc.GetMultiLineTextExtent("0").y - 2; - int values_size = (int)m_values.size(); - - if (m_ruler.long_step < 0) { - // sequential print when long_step wasn't detected because of a lot of printed objects - if (m_ruler.max_values.size() > 1) { - while (tick <= m_max_value && sequence < m_ruler.count()) { - // draw just ticks with max value - value = m_ruler.max_values[sequence]; - short_tick = tick; - - for (; tick < values_size; tick++) { - if (m_values[tick] == value) - break; - if (m_values[tick] > value) { - if (tick > 0) - tick--; - break; - } - } - if (tick > m_max_value) - break; - - wxCoord pos = get_position_from_value(tick); - draw_ticks_pair(dc, pos, mid, 5); - if (prev_y_pos < 0 || prev_y_pos - pos >= label_height) { - draw_tick_text(dc, wxPoint(mid, pos), tick); - prev_y_pos = pos; - } - draw_short_ticks(dc, short_tick, tick); - - sequence++; - tick++; - } - } - // very short object or some non-trivial ruler with non-regular step (see https://github.com/prusa3d/PrusaSlicer/issues/7263) - else { - if (get_scroll_step() < 1) // step less then 1 px indicates very tall object with non-regular laayer step (probably in vase mode) - return; - for (size_t tick = 1; tick < m_values.size(); tick++) { - wxCoord pos = get_position_from_value(tick); - draw_ticks_pair(dc, pos, mid, 5); - draw_tick_text(dc, wxPoint(mid, pos), tick); - } - } - } - else { - while (tick <= m_max_value) { - value += m_ruler.long_step; - - if (sequence < m_ruler.count() && value > m_ruler.max_values[sequence]) - value = m_ruler.max_values[sequence]; - - short_tick = tick; - - for (; tick < values_size; tick++) { - if (m_values[tick] == value) - break; - if (m_values[tick] > value) { - if (tick > 0) - tick--; - break; - } - } - if (tick > m_max_value) - break; - - wxCoord pos = get_position_from_value(tick); - draw_ticks_pair(dc, pos, mid, 5); - if (prev_y_pos < 0 || prev_y_pos - pos >= label_height) { - draw_tick_text(dc, wxPoint(mid, pos), tick); - prev_y_pos = pos; - } - - draw_short_ticks(dc, short_tick, tick); - - if (sequence < m_ruler.count() && value == m_ruler.max_values[sequence]) { - value = 0.0; - sequence++; - tick++; - } - } - // short ticks from the last tick to the end - draw_short_ticks(dc, short_tick, m_max_value); - } - - dc.SetTextForeground(old_clr); -} - -void Control::draw_one_layer_icon(wxDC& dc) -{ - if (m_draw_mode == dmSequentialGCodeView) - return; - - //const wxBitmap& icon = m_is_one_layer ? - // m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - // m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); - const ScalableBitmap& icon = m_is_one_layer ? - m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off : m_bmp_one_layer_lock_on : - m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off : m_bmp_one_layer_unlock_on; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; - is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - - dc.DrawBitmap(icon.bmp().GetBitmapFor(this), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); -} - -void Control::draw_revert_icon(wxDC& dc) -{ - if (m_ticks.empty() || m_draw_mode != dmRegular) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; - is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - - dc.DrawBitmap(m_bmp_revert.get_bitmap(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); -} - -void Control::draw_cog_icon(wxDC& dc) -{ - if (m_draw_mode == dmSequentialGCodeView) - return; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - if (m_draw_mode == dmSequentialGCodeView) { - is_horizontal() ? x_draw = width - 2 : x_draw = 0.5 * width - 0.5 * m_cog_icon_dim; - is_horizontal() ? y_draw = 0.5 * height - 0.5 * m_cog_icon_dim : y_draw = height - 2; - } - else { - is_horizontal() ? x_draw = width - 2 : x_draw = width - m_cog_icon_dim - 2; - is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2; - } - - dc.DrawBitmap(m_bmp_cog.get_bitmap(), x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); -} - -void Control::update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection) -{ - const wxRect rect = is_horizontal() ? - wxRect(begin_x + (selection == ssHigher ? m_thumb_size.x / 2 : 0), begin_y, m_thumb_size.x / 2, m_thumb_size.y) : - wxRect(begin_x, begin_y + (selection == ssLower ? m_thumb_size.y / 2 : 0), m_thumb_size.x, m_thumb_size.y / 2); - - if (selection == ssLower) - m_rect_lower_thumb = rect; - else - m_rect_higher_thumb = rect; -} - -int Control::get_value_from_position(const wxCoord x, const wxCoord y) -{ - const int height = get_size().y; - const double step = get_scroll_step(); - - if (is_horizontal()) - return int(double(x - SLIDER_MARGIN) / step + 0.5); - - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); -} - bool Control::is_lower_thumb_editable() { if (m_draw_mode == dmSequentialGCodeView) @@ -1721,113 +803,15 @@ bool Control::is_lower_thumb_editable() return true; } -bool Control::detect_selected_slider(const wxPoint& pt) -{ - if (is_point_in_rect(pt, m_rect_lower_thumb)) - m_selection = is_lower_thumb_editable() ? ssLower : ssUndef; - else if(is_point_in_rect(pt, m_rect_higher_thumb)) - m_selection = ssHigher; - else - return false; // pt doesn't referenced to any thumb - return true; -} - -bool Control::is_point_in_rect(const wxPoint& pt, const wxRect& rect) -{ - return rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && - rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); -} - -int Control::get_tick_near_point(const wxPoint& pt) -{ - for (auto tick : m_ticks.ticks) { - const wxCoord pos = get_position_from_value(tick.tick); - - if (is_horizontal()) { - if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick.tick; - } - else { - if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick.tick; - } - } - return -1; -} - void Control::ChangeOneLayerLock() { - m_is_one_layer = !m_is_one_layer; - imgui_ctrl.CombineThumbs(m_is_one_layer); - if (!m_selection || m_is_one_layer) - m_selection = ssHigher; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - // if (!m_selection) m_selection = ssHigher; - - Refresh(); - Update(); + imgui_ctrl.CombineThumbs(imgui_ctrl.IsCombineThumbs()); wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); } -void Control::OnLeftDown(wxMouseEvent& event) -{ - if (HasCapture()) - return; - this->CaptureMouse(); - - m_is_left_down = true; - m_mouse = maNone; - - wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - - if (is_point_in_rect(pos, m_rect_one_layer_icon)) - m_mouse = maOneLayerIconClick; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - m_mouse = maCogIconClick; - else if (m_draw_mode == dmRegular) { - if (is_point_in_rect(pos, m_rect_tick_action)) { - auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); - m_mouse = it == m_ticks.ticks.end() ? maAddTick : maDeleteTick; - } - else if (is_point_in_rect(pos, m_rect_revert_icon)) - m_mouse = maRevertIconClick; - } - - if (m_mouse == maNone) - detect_selected_slider(pos); - - event.Skip(); -} - -void Control::correct_lower_value() -{ - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; - - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) - m_higher_value = m_lower_value; - - imgui_ctrl.SetSelectionSpan(m_lower_value, m_higher_value); -} - -void Control::correct_higher_value() -{ - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; - - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) - m_lower_value = m_higher_value; - - imgui_ctrl.SetSelectionSpan(m_lower_value, m_higher_value); -} - wxString Control::get_tooltip(int tick/*=-1*/) { if (m_focus == fiNone) @@ -1959,96 +943,11 @@ wxString Control::get_tooltip(int tick/*=-1*/) } -int Control::get_edited_tick_for_position(const wxPoint pos, Type type /*= ColorChange*/) -{ - if (m_ticks.empty()) - return -1; - - int tick = get_value_from_position(pos); - auto it = std::lower_bound(m_ticks.ticks.begin(), m_ticks.ticks.end(), TickCode{ tick }); - - while (it != m_ticks.ticks.begin()) { - --it; - if (it->type == type) - return it->tick; - } - - return -1; -} - -void Control::OnMotion(wxMouseEvent& event) -{ - bool action = false; - - const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - int tick = -1; - - if (!m_is_left_down && !m_is_right_down) { - if (is_point_in_rect(pos, m_rect_one_layer_icon)) - m_focus = fiOneLayerIcon; - else if (is_point_in_rect(pos, m_rect_tick_action)) { - m_focus = fiActionIcon; - tick = m_selection == ssLower ? m_lower_value : m_higher_value; - } - else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - m_focus = fiRevertIcon; - else if (is_point_in_rect(pos, m_rect_cog_icon)) - m_focus = fiCogIcon; - else if (m_mode == SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && - get_edited_tick_for_position(pos) >= 0 ) - m_focus = fiColorBand; - else if (is_point_in_rect(pos, m_rect_lower_thumb)) - m_focus = fiLowerThumb; - else if (is_point_in_rect(pos, m_rect_higher_thumb)) - m_focus = fiHigherThumb; - else { - tick = get_tick_near_point(pos); - if (tick < 0 && m_is_wipe_tower) { - tick = get_value_from_position(pos); - m_focus = tick > 0 && is_wipe_tower_layer(tick) && (tick == m_lower_value || tick == m_higher_value) ? - fiSmartWipeTower : fiTick; - } - else - m_focus = fiTick; - } - m_moving_pos = pos; - } - else if (m_is_left_down || m_is_right_down) { - if (m_selection == ssLower) { - int current_value = m_lower_value; - m_lower_value = get_value_from_position(pos.x, pos.y); - correct_lower_value(); - action = (current_value != m_lower_value); - } - else if (m_selection == ssHigher) { - int current_value = m_higher_value; - m_higher_value = get_value_from_position(pos.x, pos.y); - correct_higher_value(); - action = (current_value != m_higher_value); - } - m_moving_pos = wxDefaultPosition; - } - Refresh(); - Update(); - event.Skip(); - - // Set tooltips with information for each icon - if (GUI::wxGetApp().is_editor()) - this->SetToolTip(get_tooltip(tick)); - - if (action) { - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - e.SetString("moving"); - ProcessWindowEvent(e); - } -} - void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + std::array active_extruders = get_active_extruders_for_tick(imgui_ctrl.GetActiveValue()); std::vector icons = get_extruder_color_icons(true); @@ -2079,7 +978,7 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + int tick = imgui_ctrl.GetActiveValue(); std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); wxMenu* add_color_change_menu = new wxMenu(); @@ -2103,53 +1002,6 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren } } -void Control::OnLeftUp(wxMouseEvent& event) -{ - if (!HasCapture()) - return; - this->ReleaseMouse(); - m_is_left_down = false; - - switch (m_mouse) { - case maNone : - move_current_thumb_to_pos(event.GetLogicalPosition(wxClientDC(this))); - break; - case maDeleteTick : - delete_current_tick(); - break; - case maAddTick : - add_current_tick(); - break; - case maCogIconClick : - show_cog_icon_context_menu(); - break; - case maOneLayerIconClick: - switch_one_layer_mode(); - break; - case maRevertIconClick: - discard_all_thicks(); - break; - default : - break; - } - - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void Control::enter_window(wxMouseEvent& event, const bool enter) -{ - m_is_focused = enter; - Refresh(); - Update(); - event.Skip(); -} - // "condition" have to be true for: // - value increase (if wxSL_VERTICAL) // - value decrease (if wxSL_HORIZONTAL) @@ -2157,7 +1009,7 @@ void Control::move_current_thumb(const bool condition) { // m_is_one_layer = wxGetKeyState(WXK_CONTROL); int delta = condition ? -1 : 1; - if (is_horizontal()) + if (imgui_ctrl.is_horizontal()) delta *= -1; // accelerators @@ -2169,45 +1021,16 @@ void Control::move_current_thumb(const bool condition) if (accelerator > 0) delta *= accelerator; - if (m_selection == ssUndef) - m_selection = ssHigher; - - if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); - } - Refresh(); - Update(); + imgui_ctrl.MoveActiveThumb(delta); wxCommandEvent e(wxEVT_SCROLL_CHANGED); e.SetEventObject(this); ProcessWindowEvent(e); } -void Control::OnWheel(wxMouseEvent& event) +void Control::UseDefaultColors(bool def_colors_on) { - // Set nearest to the mouse thumb as a selected, if there is not selected thumb - if (m_selection == ssUndef) { - const wxPoint& pt = event.GetLogicalPosition(wxClientDC(this)); - - if (is_horizontal()) - m_selection = abs(pt.x - m_rect_lower_thumb.GetRight()) <= - abs(pt.x - m_rect_higher_thumb.GetLeft()) ? - ssLower : ssHigher; - else - m_selection = abs(pt.y - m_rect_lower_thumb.GetTop()) <= - abs(pt.y - m_rect_higher_thumb.GetBottom()) ? - ssLower : ssHigher; - } - - if (m_selection == ssLower && !is_lower_thumb_editable()) - m_selection = ssUndef; - - move_current_thumb((m_draw_mode == dmSequentialGCodeView) ? event.GetWheelRotation() < 0 : event.GetWheelRotation() > 0); + m_ticks.set_default_colors(def_colors_on); } void Control::OnKeyDown(wxKeyEvent &event) @@ -2227,58 +1050,16 @@ void Control::OnKeyDown(wxKeyEvent &event) } else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT) UseDefaultColors(false); - else if (is_horizontal()) { - if (m_is_focused) { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN) { - if (key == WXK_DOWN) - m_selection = ssHigher; - else if (key == WXK_UP && is_lower_thumb_editable()) - m_selection = ssLower; - Refresh(); - } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - } - } - else { - if (m_is_focused) { - if (key == WXK_LEFT || key == WXK_RIGHT) { - if (key == WXK_LEFT) - m_selection = ssHigher; - else if (key == WXK_RIGHT && is_lower_thumb_editable()) - m_selection = ssLower; - Refresh(); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } - else { - if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } + else if (imgui_ctrl.is_horizontal()) { + if (key == WXK_LEFT || key == WXK_RIGHT) + move_current_thumb(key == WXK_LEFT); } + else if (key == WXK_UP || key == WXK_DOWN) + move_current_thumb(key == WXK_UP); event.Skip(); // !Needed to have EVT_CHAR generated as well } -void Control::OnKeyUp(wxKeyEvent &event) -{ - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - else if (event.GetKeyCode() == WXK_SHIFT) - UseDefaultColors(true); - - imgui_ctrl.CombineThumbs(m_is_one_layer); - - Refresh(); - Update(); - event.Skip(); -} - void Control::OnChar(wxKeyEvent& event) { const int key = event.GetKeyCode(); @@ -2296,42 +1077,6 @@ void Control::OnChar(wxKeyEvent& event) jump_to_value(); } -void Control::OnRightDown(wxMouseEvent& event) -{ - if (HasCapture() || m_is_left_down) - return; - this->CaptureMouse(); - - const wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - - m_mouse = maNone; - if (m_draw_mode == dmRegular) { - if (is_point_in_rect(pos, m_rect_tick_action)) { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - m_mouse = m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end() ? - maAddMenu : maEditMenu; - } - else if (m_mode == SingleExtruder && !detect_selected_slider(pos) && is_point_in_rect(pos, get_colored_band_rect())) - m_mouse = maForceColorEdit; - else if (m_mode == MultiAsSingle && is_point_in_rect(pos, m_rect_cog_icon)) - m_mouse = maCogIconMenu; - } - if (m_mouse != maNone || !detect_selected_slider(pos)) - return; - - if (m_selection == ssLower) - m_higher_value = m_lower_value; - else - m_lower_value = m_higher_value; - - // set slider to "one layer" mode // ysWhy??? - m_is_right_down = /*m_is_one_layer = */true; - - Refresh(); - Update(); - event.Skip(); -} - // Get active extruders for tick. // Means one current extruder for not existing tick OR // 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick) @@ -2419,93 +1164,22 @@ std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extru return used_extruders; } -void Control::show_add_context_menu() -{ - wxMenu menu; - - if (m_mode == SingleExtruder) { - append_menu_item(&menu, wxID_ANY, _L("Add color change") + " (" + gcode(ColorChange) + ")", "", - [this](wxCommandEvent&) { add_code_as_tick(ColorChange); }, "colorchange_add_m", &menu); - - UseDefaultColors(false); - } - else { - append_change_extruder_menu_item(&menu); - append_add_color_change_menu_item(&menu); - } - - if (!gcode(PausePrint).empty()) - append_menu_item(&menu, wxID_ANY, _L("Add pause print") + " (" + gcode(PausePrint) + ")", "", - [this](wxCommandEvent&) { add_code_as_tick(PausePrint); }, "pause_print", &menu); - - if (!gcode(Template).empty()) - append_menu_item(&menu, wxID_ANY, _L("Add custom template") + " (" + gcode(Template) + ")", "", - [this](wxCommandEvent&) { add_code_as_tick(Template); }, "edit_gcode", &menu); - - append_menu_item(&menu, wxID_ANY, _L("Add custom G-code"), "", - [this](wxCommandEvent&) { add_code_as_tick(Custom); }, "edit_gcode", &menu); - - GUI::wxGetApp().plater()->PopupMenu(&menu); -} - -void Control::show_edit_context_menu() -{ - wxMenu menu; - - std::set::iterator it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); - - if (it->type == ToolChange) { - if (m_mode == MultiAsSingle) - append_change_extruder_menu_item(&menu); - append_add_color_change_menu_item(&menu, true); - } - else - append_menu_item(&menu, wxID_ANY, it->type == ColorChange ? _L("Edit color") : - it->type == PausePrint ? _L("Edit pause print message") : - _L("Edit custom G-code"), "", - [this](wxCommandEvent&) { edit_tick(); }, "edit_uni", &menu); - - if (it->type == ColorChange && m_mode == MultiAsSingle) - append_change_extruder_menu_item(&menu, true); - - append_menu_item(&menu, wxID_ANY, it->type == ColorChange ? _L("Delete color change") : - it->type == ToolChange ? _L("Delete tool change") : - it->type == PausePrint ? _L("Delete pause print") : - _L("Delete custom G-code"), "", - [this](wxCommandEvent&) { delete_current_tick();}, "colorchange_del_f", &menu); - - GUI::wxGetApp().plater()->PopupMenu(&menu); -} - void Control::show_cog_icon_context_menu() { wxMenu menu; append_menu_item(&menu, wxID_ANY, _L("Jump to height") + " (Shift+G)", "", [this](wxCommandEvent&) { jump_to_value(); }, "", & menu); -#if 0 // old code - wxMenu* ruler_mode_menu = new wxMenu(); - if (ruler_mode_menu) { - append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("None"), _L("Hide ruler"), - [this](wxCommandEvent&) { if (m_extra_style != 0) m_extra_style = 0; }, ruler_mode_menu, - []() { return true; }, [this]() { return m_extra_style == 0; }, GUI::wxGetApp().plater()); - append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show object height"), _L("Show object height on the ruler"), - [this](wxCommandEvent&) { m_extra_style & wxSL_AUTOTICKS ? m_extra_style ^= wxSL_AUTOTICKS : m_extra_style |= wxSL_AUTOTICKS; }, ruler_mode_menu, - []() { return true; }, [this]() { return m_extra_style & wxSL_AUTOTICKS; }, GUI::wxGetApp().plater()); + append_menu_check_item(&menu, wxID_ANY, _L("Use default colors"), _L("Use default colors on color change"), + [this](wxCommandEvent&) { + UseDefaultColors(!m_ticks.used_default_colors()); }, &menu, + []() { return true; }, [this]() { return m_ticks.used_default_colors(); }, GUI::wxGetApp().plater()); - append_menu_check_item(ruler_mode_menu, wxID_ANY, _L("Show estimated print time"), _L("Show estimated print time on the ruler"), - [this](wxCommandEvent&) { m_extra_style & wxSL_VALUE_LABEL ? m_extra_style ^= wxSL_VALUE_LABEL : m_extra_style |= wxSL_VALUE_LABEL; }, ruler_mode_menu, - []() { return true; }, [this]() { return m_extra_style & wxSL_VALUE_LABEL; }, GUI::wxGetApp().plater()); - - append_submenu(&menu, ruler_mode_menu, wxID_ANY, _L("Ruler mode"), _L("Set ruler mode"), "", - []() { return true; }, this); - } -#else append_menu_check_item(&menu, wxID_ANY, _L("Show estimated print time on mouse moving"), _L("Show estimated print time on the ruler"), - [this](wxCommandEvent&) { m_extra_style& wxSL_VALUE_LABEL ? m_extra_style ^= wxSL_VALUE_LABEL : m_extra_style |= wxSL_VALUE_LABEL; }, &menu, - []() { return true; }, [this]() { return m_extra_style & wxSL_VALUE_LABEL; }, GUI::wxGetApp().plater()); -#endif + [this](wxCommandEvent&) { m_show_estimated_times = !m_show_estimated_times; }, &menu, + []() { return true; }, [this]() { return m_show_estimated_times; }, GUI::wxGetApp().plater()); + if (m_mode == MultiAsSingle && m_draw_mode == dmRegular) append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "", [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); @@ -2588,41 +1262,13 @@ void Control::auto_color_change() }); } - if (m_ticks.empty()) { + if (m_ticks.empty()) GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange); - imgui_ctrl.set_draw_scroll_line_cb(nullptr); - } - else - imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); + update_callbacks(); post_ticks_changed_event(); } -void Control::OnRightUp(wxMouseEvent& event) -{ - if (!HasCapture() || m_is_left_down) - return; - this->ReleaseMouse(); - m_is_right_down = /*m_is_one_layer = */false; - - if (m_mouse == maForceColorEdit) { - wxPoint pos = event.GetLogicalPosition(wxClientDC(this)); - int edited_tick = get_edited_tick_for_position(pos); - if (edited_tick >= 0) - edit_tick(edited_tick); - } - else if (m_mouse == maAddMenu) - show_add_context_menu(); - else if (m_mouse == maEditMenu) - show_edit_context_menu(); - else if (m_mouse == maCogIconMenu) - show_cog_icon_context_menu(); - - Refresh(); - Update(); - event.Skip(); -} - static std::string get_new_color(const std::string& color) { wxColour clr(color); @@ -2735,9 +1381,7 @@ static double get_value_to_jump(double active_value, double min_z, double max_z, void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = imgui_ctrl.GetActiveValue(); if ( !check_ticks_changed_event(type) ) return; @@ -2763,21 +1407,15 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) else return; - if (was_ticks != m_ticks.empty()) { - if (m_ticks.empty()) - imgui_ctrl.set_draw_scroll_line_cb(nullptr); - else - imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); - } + if (was_ticks != m_ticks.empty()) + update_callbacks(); post_ticks_changed_event(type); } void Control::add_current_tick(bool call_from_keyboard /*= false*/) { - if (m_selection == ssUndef) - return; - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const int tick = imgui_ctrl.GetActiveValue(); auto it = m_ticks.ticks.find(TickCode{ tick }); if (it != m_ticks.ticks.end() || // this tick is already exist @@ -2786,6 +1424,7 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) if (m_mode == SingleExtruder) add_code_as_tick(ColorChange); + /* else { wxMenu menu; @@ -2798,7 +1437,7 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) wxPoint pos = wxDefaultPosition; /* Menu position will be calculated from mouse click position, but... * if function is called from keyboard (pressing "+"), we should to calculate it - * */ + * * / if (call_from_keyboard) { int width, height; get_size(&width, &height); @@ -2813,14 +1452,12 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) GUI::wxGetApp().plater()->PopupMenu(&menu, pos); } + */ } void Control::delete_current_tick() { - if (m_selection == ssUndef) - return; - - auto it = m_ticks.ticks.find(TickCode{ m_selection == ssLower ? m_lower_value : m_higher_value }); + auto it = m_ticks.ticks.find(TickCode{ imgui_ctrl.GetActiveValue()}); if (it == m_ticks.ticks.end() || !check_ticks_changed_event(it->type)) return; @@ -2833,7 +1470,7 @@ void Control::delete_current_tick() void Control::edit_tick(int tick/* = -1*/) { if (tick < 0) - tick = m_selection == ssLower ? m_lower_value : m_higher_value; + tick = imgui_ctrl.GetActiveValue(); const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); if (it == m_ticks.ticks.end() || @@ -2848,50 +1485,16 @@ void Control::edit_tick(int tick/* = -1*/) // switch on/off one layer mode void Control::switch_one_layer_mode() { - m_is_one_layer = !m_is_one_layer; - imgui_ctrl.CombineThumbs(m_is_one_layer); - if (!m_is_one_layer) { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - } - if (!m_selection || m_is_one_layer) - m_selection = ssHigher; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); -// if (m_selection == ssUndef) m_selection = ssHigher; + imgui_ctrl.CombineThumbs(!imgui_ctrl.IsCombineThumbs()); } // discard all custom changes on DoubleSlider void Control::discard_all_thicks() { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (m_selection == ssUndef) m_selection = ssHigher; - m_ticks.ticks.clear(); - imgui_ctrl.set_draw_scroll_line_cb(nullptr); + imgui_ctrl.ResetValues(); + update_callbacks(); post_ticks_changed_event(); - -} - -// Set current thumb position to the nearest tick (if it is) -// OR to a value corresponding to the mouse click (pos) -void Control::move_current_thumb_to_pos(wxPoint pos) -{ - const int tick_val = get_tick_near_point(pos); - const int mouse_val = tick_val >= 0 && m_draw_mode == dmRegular ? tick_val : - get_value_from_position(pos); - if (mouse_val >= 0) { - if (m_selection == ssLower) { - SetLowerValue(mouse_val); - correct_lower_value(); - } - else { // even m_selection is ssUndef, upper thumb should be selected - SetHigherValue(mouse_val); - correct_higher_value(); - } - } } void Control::edit_extruder_sequence() @@ -2918,7 +1521,7 @@ void Control::edit_extruder_sequence() std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() std::uniform_int_distribution<> distrib(0, extr_cnt-1); - while (tick <= m_max_value) + while (tick <= imgui_ctrl.GetMaxValue()) { bool color_repetition = false; if (m_extruders_sequence.random_sequence) { @@ -2957,17 +1560,17 @@ void Control::edit_extruder_sequence() void Control::jump_to_value() { - double value = get_value_to_jump(m_values[m_selection == ssLower ? m_lower_value : m_higher_value], - m_values[m_min_value], m_values[m_max_value], m_draw_mode); + double value = get_value_to_jump(m_values[imgui_ctrl.GetActiveValue()], + m_values[imgui_ctrl.GetMinValue()], m_values[imgui_ctrl.GetMaxValue()], m_draw_mode); if (value < 0.0) return; int tick_value = get_tick_from_value(value); - if (m_selection == ssLower) - SetLowerValue(tick_value); - else + if (imgui_ctrl.IsActiveHigherThumb()) SetHigherValue(tick_value); + else + SetLowerValue(tick_value); } void Control::post_ticks_changed_event(Type type /*= Custom*/) @@ -3019,7 +1622,6 @@ bool Control::check_ticks_changed_event(Type type) _L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" + _L("Are you sure you want to continue?") ) ; - //wxMessageDialog msg(this, message, _L("Notice"), wxYES_NO | (m_mode == SingleExtruder ? wxCANCEL : 0)); GUI::MessageDialog msg(this, message, _L("Notice"), wxYES_NO | (m_mode == SingleExtruder ? wxCANCEL : 0)); const int answer = msg.ShowModal(); if (answer == wxID_YES) { @@ -3050,7 +1652,7 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int }; if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) { -#if 1 + if (ticks.empty()) return opposite_one_color((*m_colors)[0]); @@ -3107,14 +1709,6 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int return opposite_one_color((*m_colors)[0]); return opposite_two_colors((*m_colors)[0], frst_color); -#else - const std::vector& colors = ColorPrintColors::get(); - if (ticks.empty()) - return colors[0]; - m_default_color_idx++; - - return colors[m_default_color_idx % colors.size()]; -#endif } std::string color = (*m_colors)[extruder - 1]; @@ -3159,9 +1753,6 @@ bool TickCodeInfo::add_tick(const int tick, Type type, const int extruder, doubl return false; } - if (mode == SingleExtruder) - m_use_default_colors = true; - ticks.emplace(TickCode{ tick, type, extruder, color, extra }); return true; } diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index e67bd95447..74e4f2e08b 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -47,13 +47,8 @@ bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t // custom message the slider sends to its parent to notify a tick-change: wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); -/* -enum SelectedSlider { - ssUndef, - ssLower, - ssHigher -}; -*/ + + enum FocusedItem { fiNone, fiRevertIcon, @@ -123,8 +118,7 @@ class TickCodeInfo std::string pause_print_msg; bool m_suppress_plus = false; bool m_suppress_minus = false; - bool m_use_default_colors= false; -// int m_default_color_idx = 0; + bool m_use_default_colors{ true }; std::vector* m_colors {nullptr}; @@ -156,6 +150,7 @@ public: bool suppressed_plus () { return m_suppress_plus; } bool suppressed_minus() { return m_suppress_minus; } void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; } + bool used_default_colors() const { return m_use_default_colors; } void set_extruder_colors(std::vector* extruder_colors) { m_colors = extruder_colors; } }; @@ -214,31 +209,22 @@ class Control : public wxControl public: Control( wxWindow *parent, - wxWindowID id, int lowerValue, int higherValue, int minValue, int maxValue, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, long style = wxSL_VERTICAL, const wxString& name = wxEmptyString); ~Control() {} - void msw_rescale(); - void sys_color_changed(); - - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } - int GetActiveValue() const; + int GetMinValue() const { return imgui_ctrl.GetMinValue(); } + int GetMaxValue() const { return imgui_ctrl.GetMaxValue(); } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[GetMinValue()]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[GetMaxValue()]; } + int GetLowerValue() const { return imgui_ctrl.GetLowerValue(); } + int GetHigherValue()const { return imgui_ctrl.GetHigherValue(); } double GetLowerValueD() { return get_double_value(ssLower); } double GetHigherValueD() { return get_double_value(ssHigher); } - wxSize DoGetBestSize() const override; - wxSize get_min_size() const ; // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. void SetLowerValue (const int lower_val); @@ -246,7 +232,6 @@ public: void SetSelectionSpan(const int lower_val, const int higher_val); void SetMaxValue(const int max_value); - void SetKoefForLabels(const double koef) { m_label_koef = koef; } void SetSliderValues(const std::vector& values); void ChangeOneLayerLock(); void SetSliderAlternateValues(const std::vector& values) { m_alternate_values = values; } @@ -269,25 +254,15 @@ public: void set_render_as_disabled(bool value) { m_render_as_disabled = value; } bool is_rendering_as_disabled() const { return m_render_as_disabled; } - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } - bool is_one_layer() const { return m_is_one_layer; } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } - bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + bool is_lower_at_min() const { return imgui_ctrl.is_lower_at_min(); } + bool is_higher_at_max() const { return imgui_ctrl.is_higher_at_max(); } - void OnPaint(wxPaintEvent& ) { render(); } - void OnLeftDown(wxMouseEvent& event); - void OnMotion(wxMouseEvent& event); - void OnLeftUp(wxMouseEvent& event); - void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } - void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } - void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } - void OnWheel(wxMouseEvent& event); + void UseDefaultColors(bool def_colors_on); void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); void OnChar(wxKeyEvent &event); - void OnRightDown(wxMouseEvent& event); - void OnRightUp(wxMouseEvent& event); + + bool Show(bool show = true) override { imgui_ctrl.Show(show); return true; } + bool Hide() { return Show(false); } void add_code_as_tick(Type type, int selected_extruder = -1); // add default action for tick, when press "+" @@ -297,68 +272,28 @@ public: void edit_tick(int tick = -1); void switch_one_layer_mode(); void discard_all_thicks(); - void move_current_thumb_to_pos(wxPoint pos); void edit_extruder_sequence(); void jump_to_value(); void enable_action_icon(bool enable) { m_enable_action_icon = enable; } - void show_add_context_menu(); - void show_edit_context_menu(); void show_cog_icon_context_menu(); void auto_color_change(); void imgui_render(GUI::GLCanvas3D& canvas, float extra_scale = 1.f); -protected: - - void render(); - void draw_focus_rect(wxDC& dc); - void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); - void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); - void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); - void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); - void draw_ticks_pair(wxDC& dc, wxCoord pos, wxCoord mid, int tick_len); - void draw_ticks(wxDC& dc); - void draw_colored_band(wxDC& dc); - void draw_ruler(wxDC& dc); - void draw_one_layer_icon(wxDC& dc); - void draw_revert_icon(wxDC& dc); - void draw_cog_icon(wxDC &dc); - void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); - void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); - void draw_tick_on_mouse_position(wxDC &dc); - void draw_tick_text(wxDC& dc, const wxPoint& pos, int tick, LabelType label_type = ltHeight, bool right_side = true) const; - void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - - void update_thumb_rect(const wxCoord begin_x, const wxCoord begin_y, const SelectedSlider& selection); - bool is_lower_thumb_editable(); - bool detect_selected_slider(const wxPoint& pt); - void correct_lower_value(); - void correct_higher_value(); - void move_current_thumb(const bool condition); - void enter_window(wxMouseEvent& event, const bool enter); - bool is_wipe_tower_layer(int tick) const; - private: - bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); - int get_tick_near_point(const wxPoint& pt); + bool is_lower_thumb_editable(); + void move_current_thumb(const bool condition); + bool is_wipe_tower_layer(int tick) const; - double get_scroll_step(); wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const; - void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); } - wxCoord get_position_from_value(const int value); - wxSize get_size() const; - void get_size(int* w, int* h) const; + double get_double_value(const SelectedSlider& selection); int get_tick_from_value(double value, bool force_lower_bound = false); wxString get_tooltip(int tick = -1); - int get_edited_tick_for_position(wxPoint pos, Type type = ColorChange); std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; std::string get_color_for_color_change_tick(std::set::const_iterator it) const; - wxRect get_colored_band_rect(); // Get active extruders for tick. // Means one current extruder for not existing tick OR @@ -373,32 +308,10 @@ private: void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); bool is_osx { false }; - wxFont m_font; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; bool m_render_as_disabled{ false }; bool m_allow_editing{ true }; - ScalableBitmap m_bmp_thumb_higher; - ScalableBitmap m_bmp_thumb_lower; - ScalableBitmap m_bmp_add_tick_on; - ScalableBitmap m_bmp_add_tick_off; - ScalableBitmap m_bmp_del_tick_on; - ScalableBitmap m_bmp_del_tick_off; - ScalableBitmap m_bmp_one_layer_lock_on; - ScalableBitmap m_bmp_one_layer_lock_off; - ScalableBitmap m_bmp_one_layer_unlock_on; - ScalableBitmap m_bmp_one_layer_unlock_off; - ScalableBitmap m_bmp_revert; - ScalableBitmap m_bmp_cog; - SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; - bool m_is_one_layer = false; - bool m_is_focused = false; bool m_force_mode_apply = true; bool m_enable_action_icon = true; bool m_is_wipe_tower = false; //This flag indicates that there is multiple extruder print with wipe tower @@ -410,22 +323,8 @@ private: MouseAction m_mouse = maNone; FocusedItem m_focus = fiNone; - wxPoint m_moving_pos = wxDefaultPosition; - wxRect m_rect_lower_thumb; - wxRect m_rect_higher_thumb; - wxRect m_rect_tick_action; - wxRect m_rect_one_layer_icon; - wxRect m_rect_revert_icon; - wxRect m_rect_cog_icon; - wxSize m_thumb_size; - int m_tick_icon_dim; - int m_lock_icon_dim; - int m_revert_icon_dim; - int m_cog_icon_dim; - long m_style; - long m_extra_style; - float m_label_koef{ 1.0 }; + bool m_show_estimated_times{ false }; std::vector m_values; TickCodeInfo m_ticks; @@ -438,63 +337,17 @@ private: ExtrudersSequence m_extruders_sequence; -// control's view variables - wxCoord SLIDER_MARGIN; // margin around slider - - wxPen DARK_ORANGE_PEN; - wxPen ORANGE_PEN; - wxPen LIGHT_ORANGE_PEN; - - wxPen DARK_GREY_PEN; - wxPen GREY_PEN; - wxPen LIGHT_GREY_PEN; - - wxPen FOCUS_RECT_PEN; - wxBrush FOCUS_RECT_BRUSH; - - std::vector m_line_pens; - std::vector m_segm_pens; - - class Ruler { - wxWindow* m_parent{nullptr}; // m_parent is nullptr for Unused ruler - // in this case we will not init/update/render it - // values to check if ruler has to be updated - double m_min_val; - double m_max_val; - double m_scroll_step; - size_t m_max_values_cnt; - int m_DPI; - - public: - - double long_step; - double short_step; - std::vector max_values;// max value for each object/instance in sequence print - // > 1 for sequential print - - void set_parent(wxWindow* parent); - void update_dpi(); - void init(const std::vector& values, double scroll_step); - void update(const std::vector& values, double scroll_step); - bool is_ok() { return long_step > 0 && short_step > 0; } - size_t count() { return max_values.size(); } - bool can_draw() { return m_parent != nullptr; } - } m_ruler; - // ImGuiDS - float m_scale{ 1.0 }; + float m_scale{ 1.f }; bool m_can_change_color{ true }; - bool m_show_menu{ false }; void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); void render_menu(); bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1); - - + void update_callbacks(); GUI::ImGuiControl imgui_ctrl; - }; } // DoubleSlider; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d755272a1b..35b1e3b284 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -231,29 +231,16 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_canvas->show_legend(true); m_canvas->enable_dynamic_background(true); - m_layers_slider_sizer = create_layers_slider_sizer(); - - wxGetApp().UpdateDarkUI(m_bottom_toolbar_panel = new wxPanel(this)); + create_layers_slider(); m_left_sizer = new wxBoxSizer(wxVERTICAL); m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); - wxBoxSizer* right_sizer = new wxBoxSizer(wxVERTICAL); - right_sizer->Add(m_layers_slider_sizer, 1, wxEXPAND, 0); - - m_moves_slider = new DoubleSlider::Control(m_bottom_toolbar_panel, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL, "moves_slider"); + m_moves_slider = new DoubleSlider::Control(this, 0, 0, 0, 100, wxSL_HORIZONTAL, "moves_slider"); m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); - wxBoxSizer* bottom_toolbar_sizer = new wxBoxSizer(wxHORIZONTAL); - bottom_toolbar_sizer->Add(m_moves_slider, 1, wxALL | wxEXPAND, 0); - m_bottom_toolbar_panel->SetSizer(bottom_toolbar_sizer); - - m_left_sizer->Add(m_bottom_toolbar_panel, 0, wxALL | wxEXPAND, 0); - m_left_sizer->Hide(m_bottom_toolbar_panel); - wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL); main_sizer->Add(m_left_sizer, 1, wxALL | wxEXPAND, 0); - main_sizer->Add(right_sizer, 0, wxALL | wxEXPAND, 0); SetSizer(main_sizer); SetMinSize(GetSize()); @@ -325,10 +312,6 @@ void Preview::reload_print() void Preview::msw_rescale() { - // rescale slider - if (m_layers_slider != nullptr) m_layers_slider->msw_rescale(); - if (m_moves_slider != nullptr) m_moves_slider->msw_rescale(); - // rescale warning legend on the canvas get_canvas3d()->msw_rescale(); @@ -336,23 +319,11 @@ void Preview::msw_rescale() reload_print(); } -void Preview::sys_color_changed() -{ -#ifdef _WIN32 - wxWindowUpdateLocker noUpdates(this); - wxGetApp().UpdateAllStaticTextDarkUI(m_bottom_toolbar_panel); -#endif // _WIN32 - - if (m_layers_slider != nullptr) - m_layers_slider->sys_color_changed(); -} - - void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) { - if (m_layers_slider && m_layers_slider->IsShown()) + if (m_layers_slider) m_layers_slider->imgui_render(canvas, extra_scale); - if (m_moves_slider && m_moves_slider->IsShown() && m_bottom_toolbar_panel->IsShown()) + if (m_moves_slider) m_moves_slider->imgui_render(canvas, extra_scale); } @@ -390,8 +361,7 @@ void Preview::move_moves_slider(wxKeyEvent& evt) void Preview::hide_layers_slider() { - m_layers_slider_sizer->Hide((size_t)0); - Layout(); + m_layers_slider->Hide(); } void Preview::on_size(wxSizeEvent& evt) @@ -400,16 +370,14 @@ void Preview::on_size(wxSizeEvent& evt) Refresh(); } -wxBoxSizer* Preview::create_layers_slider_sizer() +void Preview::create_layers_slider() { - wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - m_layers_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxVERTICAL, "layers_slider"); + m_layers_slider = new DoubleSlider::Control(this,0, 0, 0, 100, wxVERTICAL, "layers_slider"); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); - m_layers_slider->enable_action_icon(wxGetApp().is_editor()); - sizer->Add(m_layers_slider, 0, wxEXPAND, 0); + m_layers_slider->enable_action_icon(wxGetApp().is_editor()); // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this); @@ -429,8 +397,6 @@ wxBoxSizer* Preview::create_layers_slider_sizer() m_keep_current_preview_type = false; reload_print(); }); - - return sizer; } // Find an index of a value in a sorted vector, which is in . @@ -602,9 +568,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee break; } } - - m_layers_slider_sizer->Show((size_t)0); - Layout(); + m_layers_slider->Show(); } void Preview::update_layers_slider_mode() @@ -679,12 +643,10 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) if (key == 'S' || key == 'W') { const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1; m_layers_slider->SetHigherValue(new_pos); - if (event.ShiftDown() || m_layers_slider->is_one_layer()) m_layers_slider->SetLowerValue(m_layers_slider->GetHigherValue()); } else if (key == 'A' || key == 'D') { const int new_pos = key == 'D' ? m_moves_slider->GetHigherValue() + 1 : m_moves_slider->GetHigherValue() - 1; m_moves_slider->SetHigherValue(new_pos); - if (event.ShiftDown() || m_moves_slider->is_one_layer()) m_moves_slider->SetLowerValue(m_moves_slider->GetHigherValue()); } else if (key == 'X') m_layers_slider->ChangeOneLayerLock(); @@ -790,9 +752,7 @@ void Preview::load_print_as_fff(bool keep_z_range) if (wxGetApp().is_editor() && !has_layers) { m_canvas->reset_gcode_layers_times_cache(); hide_layers_slider(); - m_left_sizer->Hide(m_bottom_toolbar_panel); - m_left_sizer->Layout(); - Refresh(); + m_moves_slider->Hide(); m_canvas_widget->Refresh(); return; } @@ -819,11 +779,9 @@ void Preview::load_print_as_fff(bool keep_z_range) m_canvas->load_gcode_preview(*m_gcode_result, tool_colors, color_print_colors); // the view type may have been changed by the call m_canvas->load_gcode_preview() gcode_view_type = m_canvas->get_gcode_view_type(); - m_left_sizer->Layout(); - Refresh(); zs = m_canvas->get_gcode_layers_zs(); if (!zs.empty()) - m_left_sizer->Show(m_bottom_toolbar_panel); + m_moves_slider->Show(); m_loaded = true; } else if (is_pregcode_preview) { @@ -832,15 +790,11 @@ void Preview::load_print_as_fff(bool keep_z_range) // the view type has been changed by the call m_canvas->load_gcode_preview() if (gcode_view_type == libvgcode::EViewType::ColorPrint && !color_print_values.empty()) m_canvas->set_gcode_view_type(gcode_view_type); - m_left_sizer->Hide(m_bottom_toolbar_panel); - m_left_sizer->Layout(); - Refresh(); + m_moves_slider->Hide(); zs = m_canvas->get_gcode_layers_zs(); } else { - m_left_sizer->Hide(m_bottom_toolbar_panel); - m_left_sizer->Layout(); - Refresh(); + m_moves_slider->Hide(); } if (!zs.empty() && !m_keep_current_preview_type) { @@ -902,9 +856,7 @@ void Preview::load_print_as_sla() if (IsShown()) { m_canvas->load_sla_preview(); - m_left_sizer->Hide(m_bottom_toolbar_panel); - m_left_sizer->Layout(); - Refresh(); + m_moves_slider->Hide(); if (n_layers > 0) update_layers_slider(zs); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 1b9da04f30..90484a8a0b 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -82,8 +82,6 @@ class Preview : public wxPanel wxGLCanvas* m_canvas_widget { nullptr }; GLCanvas3D* m_canvas { nullptr }; wxBoxSizer* m_left_sizer { nullptr }; - wxBoxSizer* m_layers_slider_sizer { nullptr }; - wxPanel* m_bottom_toolbar_panel { nullptr }; DynamicPrintConfig* m_config; BackgroundSlicingProcess* m_process; @@ -135,7 +133,6 @@ public: void reload_print(); void msw_rescale(); - void sys_color_changed(); void jump_layers_slider(wxKeyEvent& evt); void move_layers_slider(wxKeyEvent& evt); void edit_layers_slider(wxKeyEvent& evt); @@ -162,7 +159,7 @@ private: void on_size(wxSizeEvent& evt); // Create/Update/Reset double slider on 3dPreview - wxBoxSizer* create_layers_slider_sizer(); + void create_layers_slider(); void check_layers_slider_values(std::vector& ticks_from_model, const std::vector& layers_z); void reset_layers_slider(); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index c68097a922..ab65590d80 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -148,18 +148,22 @@ void ImGuiControl::SetLowerValue(const int lower_val) { m_selection = ssLower; m_lower_value = lower_val; + correct_lower_value(); } void ImGuiControl::SetHigherValue(const int higher_val) { m_selection = ssHigher; m_higher_value = higher_val; + correct_higher_value(); } void ImGuiControl::SetSelectionSpan(const int lower_val, const int higher_val) { m_lower_value = std::max(lower_val, m_min_value); m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); + if (m_lower_value < m_higher_value) + m_combine_thumbs = false; } void ImGuiControl::SetMaxValue(const int max_value) @@ -167,16 +171,61 @@ void ImGuiControl::SetMaxValue(const int max_value) m_max_value = max_value; } -void ImGuiControl::SetSliderValues(const std::vector& values) +void ImGuiControl::MoveActiveThumb(int delta) { - m_values = values; + if (m_selection == ssUndef) + m_selection = ssHigher; + + if (m_selection == ssLower) { + m_lower_value -= delta; + correct_lower_value(); + } + else if (m_selection == ssHigher) { + m_higher_value -= delta; + correct_higher_value(); + } +} + +void ImGuiControl::correct_lower_value() +{ + if (m_lower_value < m_min_value) + m_lower_value = m_min_value; + else if (m_lower_value > m_max_value) + m_lower_value = m_max_value; + + if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_combine_thumbs) { + m_higher_value = m_lower_value; + } +} + +void ImGuiControl::correct_higher_value() +{ + if (m_higher_value > m_max_value) + m_higher_value = m_max_value; + else if (m_higher_value < m_min_value) + m_higher_value = m_min_value; + + if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_combine_thumbs) { + m_lower_value = m_higher_value; + } } void ImGuiControl::CombineThumbs(bool combine) { m_combine_thumbs = combine; - if (combine) - m_selection = ssHigher; + if (combine) { + m_selection = ssHigher; + correct_higher_value(); + } + else + ResetValues(); +} + +void ImGuiControl::ResetValues() +{ + SetLowerValue(m_min_value); + SetHigherValue(m_max_value); + m_selection == ssLower ? correct_lower_value() : correct_higher_value(); } std::string ImGuiControl::get_label(int pos) const @@ -184,16 +233,10 @@ std::string ImGuiControl::get_label(int pos) const if (m_cb_get_label) return m_cb_get_label(pos); - const size_t value = pos; - - if (m_label_koef == 1.0 && m_values.empty()) - return std::to_string(static_cast(value)); - if (value >= m_values.size()) + if (pos >= m_max_value || pos < m_min_value) return "ErrVal"; - return m_values.empty() ? - to_string_with_precision(m_label_koef * value) : - to_string_with_precision(m_values[value]); + return std::to_string(pos); } float ImGuiControl::GetPositionFromValue(int value, const ImRect& rect) const @@ -457,7 +500,7 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, return value_changed; } -bool ImGuiControl::render(SelectedSlider& selection) +bool ImGuiControl::render() { bool result = false; @@ -488,13 +531,15 @@ bool ImGuiControl::render(SelectedSlider& selection) int temp_lower_value = m_lower_value; if (draw_slider(&higher_value, &lower_value, higher_label, lower_label, m_pos, m_size, scale)) { - if (temp_higher_value != higher_value) - SetHigherValue(higher_value); + if (temp_higher_value != higher_value) { + m_higher_value = higher_value; + if (m_combine_thumbs) + m_lower_value = m_higher_value; + } if (temp_lower_value != lower_value) - SetLowerValue(lower_value); + m_lower_value = lower_value; result = true; } - selection = m_selection; ImGuiPureWrap::end(); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 5683fc64bd..425a33d8c7 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -56,8 +56,6 @@ public: int GetMinValue() const { return m_min_value; } int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } int GetLowerValue() const { return m_lower_value; } int GetHigherValue() const { return m_higher_value; } int GetActiveValue() const; @@ -69,8 +67,8 @@ public: void SetSelectionSpan(const int lower_val, const int higher_val); void SetMaxValue(const int max_value); - void SetSliderValues(const std::vector& values); void CombineThumbs(bool combine); + void ResetValues(); void SetPos(ImVec2 pos) { m_pos = pos; } void SetSize(ImVec2 size) { m_size = size; } @@ -80,6 +78,14 @@ public: m_size = size; m_draw_opts.scale = scale; } + + void Show(bool show) { m_is_shown = show; } + void Hide() { m_is_shown = false; } + bool IsShown() const { return m_is_shown; } + void MoveActiveThumb(int delta); + bool IsCombineThumbs() const { return m_combine_thumbs; } + bool IsActiveHigherThumb() const { return m_selection == ssHigher; } + void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); } @@ -89,7 +95,10 @@ public: bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } bool is_rclick_on_thumb() const { return m_rclick_on_selected_thumb; } - bool render(SelectedSlider& selection); + void correct_lower_value(); + void correct_higher_value(); + + bool render(); void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); std::string get_label(int pos) const; @@ -132,6 +141,7 @@ private: ImVec2 m_size; std::string m_name; ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None }; + bool m_is_shown{ true }; int m_min_value; int m_max_value; @@ -139,14 +149,15 @@ private: int m_higher_value; int m_mouse_pos_value; - double m_label_koef{ 1. }; - bool m_rclick_on_selected_thumb{ false }; bool m_draw_lower_thumb{ true }; bool m_combine_thumbs { false }; bool m_show_move_label{ false }; + DrawOptions m_draw_opts; + Regions m_regions; + std::function m_cb_get_label { nullptr }; std::function m_cb_get_label_on_move { nullptr }; std::function m_cb_draw_scroll_line { nullptr }; @@ -163,13 +174,6 @@ private: bool draw_slider(int* higher_value, int* lower_value, std::string& higher_label, std::string& lower_label, const ImVec2& pos, const ImVec2& size, float scale = 1.0f); - -protected: - - std::vector m_values; - - DrawOptions m_draw_opts; - Regions m_regions; }; } // GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b32d44e55b..da95c696c2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6698,7 +6698,6 @@ void Plater::msw_rescale() void Plater::sys_color_changed() { - p->preview->sys_color_changed(); p->sidebar->sys_color_changed(); p->menus.sys_color_changed(); From 56a3740fe433df5bb493f45e207413cde891eab2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Sat, 23 Mar 2024 08:28:08 +0100 Subject: [PATCH 07/13] ImguiDoubleSlider: WIP: Control is split to DSManagerForGcode and DSManagerForLayers (both are derived from template class) + GLCanvas3D: refactoring for handle of events related to the manipulation with sliders + use callbacks to process navigation in sliders instead of wxEvents --- src/slic3r/GUI/DoubleSlider.cpp | 460 +++++++--------------- src/slic3r/GUI/DoubleSlider.hpp | 237 +++++++---- src/slic3r/GUI/ExtruderSequenceDialog.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 13 +- src/slic3r/GUI/GLCanvas3D.hpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 136 ++++--- src/slic3r/GUI/GUI_Preview.hpp | 19 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 4 - src/slic3r/GUI/ImGuiDoubleSlider.hpp | 56 ++- src/slic3r/GUI/Plater.cpp | 7 +- 10 files changed, 418 insertions(+), 519 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index f1f2b5e31d..0823a58977 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -17,16 +17,9 @@ #include "Tab.hpp" #include "GUI_ObjectList.hpp" -#include #include -#include -#include #include -#include -#include -#include #include -#include #include #include @@ -60,8 +53,6 @@ bool equivalent_areas(const double& bottom_area, const double& top_area) return fabs(bottom_area - top_area) <= miscalculation; } -wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - static std::string gcode(Type type) { const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); @@ -73,88 +64,7 @@ static std::string gcode(Type type) } } -Control::Control( wxWindow *parent, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - long style, - const wxString& name) : - wxControl(parent, wxID_ANY, wxDefaultPosition, wxSize(0,0)), - m_allow_editing(wxGetApp().is_editor()), - m_show_estimated_times(style == wxSL_VERTICAL) -{ -#ifdef __WXOSX__ - is_osx = true; -#endif //__WXOSX__ - - m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); - m_ticks.set_extruder_colors(&m_extruder_colors); - - imgui_ctrl = ImGuiControl( lowerValue, higherValue, - minValue, maxValue, - ImVec2(0.f, 0.f), ImVec2(0.f, 0.f), - style == wxSL_VERTICAL ? ImGuiSliderFlags_Vertical : 0, - into_u8(name), style == wxSL_VERTICAL); - - imgui_ctrl.set_get_label_cb([this](int pos) {return into_u8(get_label(pos)); }); - - if (!imgui_ctrl.is_horizontal()) { - imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); - imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); - } - -} - -void Control::SetLowerValue(const int lower_val) -{ - imgui_ctrl.SetLowerValue(lower_val); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void Control::SetHigherValue(const int higher_val) -{ - imgui_ctrl.SetHigherValue(higher_val); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void Control::SetSelectionSpan(const int lower_val, const int higher_val) -{ - imgui_ctrl.SetSelectionSpan(lower_val, higher_val); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void Control::SetMaxValue(const int max_value) -{ - imgui_ctrl.SetMaxValue(max_value); -} - -void Control::SetSliderValues(const std::vector& values) -{ - m_values = values; -} - -double Control::get_double_value(const SelectedSlider& selection) -{ - if (m_values.empty() || imgui_ctrl.GetLowerValue() < 0) - return 0.0; - if (m_values.size() <= size_t(imgui_ctrl.GetHigherValue())) { - imgui_ctrl.correct_higher_value(); - return m_values.back(); - } - return m_values[selection == ssLower ? imgui_ctrl.GetLowerValue() : imgui_ctrl.GetHigherValue()]; -} - -int Control::get_tick_from_value(double value, bool force_lower_bound/* = false*/) +int DSManagerForLayers::get_tick_from_value(double value, bool force_lower_bound/* = false*/) { std::vector::iterator it; if (m_is_wipe_tower && !force_lower_bound) @@ -168,7 +78,7 @@ int Control::get_tick_from_value(double value, bool force_lower_bound/* = false* return int(it - m_values.begin()); } -Info Control::GetTicksValues() const +Info DSManagerForLayers::GetTicksValues() const { Info custom_gcode_per_print_z; std::vector& values = custom_gcode_per_print_z.gcodes; @@ -187,7 +97,7 @@ Info Control::GetTicksValues() const return custom_gcode_per_print_z; } -void Control::SetTicksValues(const Info& custom_gcode_per_print_z) +void DSManagerForLayers::SetTicksValues(const Info& custom_gcode_per_print_z) { if (m_values.empty()) { m_ticks.mode = m_mode; @@ -217,7 +127,7 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) m_ticks.mode = custom_gcode_per_print_z.mode; } -void Control::SetLayersTimes(const std::vector& layers_times, float total_time) +void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times, float total_time) { m_layers_times.clear(); if (layers_times.empty()) @@ -243,7 +153,7 @@ void Control::SetLayersTimes(const std::vector& layers_times, float total } } -void Control::SetLayersTimes(const std::vector& layers_times) +void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times) { m_is_wipe_tower = false; m_layers_times = layers_times; @@ -251,15 +161,16 @@ void Control::SetLayersTimes(const std::vector& layers_times) m_layers_times[i] += m_layers_times[i - 1]; } -void Control::SetDrawMode(bool is_sla_print, bool is_sequential_print) +void DSManagerForLayers::SetDrawMode(bool is_sla_print, bool is_sequential_print) { m_draw_mode = is_sla_print ? dmSlaPrint : is_sequential_print ? dmSequentialFffPrint : - dmRegular; + dmRegular; + update_callbacks(); } -void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) +void DSManagerForLayers::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) { m_mode = !is_one_extruder_printed_model ? MultiExtruder : only_extruder < 0 ? SingleExtruder : @@ -274,28 +185,21 @@ void Control::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, c m_is_wipe_tower = m_mode != SingleExtruder; } -void Control::SetExtruderColors( const std::vector& extruder_colors) +void DSManagerForLayers::SetExtruderColors( const std::vector& extruder_colors) { m_extruder_colors = extruder_colors; } -bool Control::IsNewPrint() +bool DSManagerForLayers::IsNewPrint(const std::string& idxs) { - if (GUI::wxGetApp().plater()->printer_technology() == ptSLA) - return false; - const Print& print = GUI::wxGetApp().plater()->fff_print(); - std::string idxs; - for (auto object : print.objects()) - idxs += std::to_string(object->id().id) + "_"; - - if (idxs == m_print_obj_idxs) + if (idxs == "sla" || idxs == m_print_obj_idxs) return false; m_print_obj_idxs = idxs; return true; } -void Control::update_callbacks() +void DSManagerForLayers::update_callbacks() { if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) imgui_ctrl.set_draw_scroll_line_cb(nullptr); @@ -305,7 +209,7 @@ void Control::update_callbacks() using namespace ImGui; -void Control::draw_ticks(const ImRect& slideable_region) +void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) { //if(m_draw_mode != dmRegular) // return; @@ -414,7 +318,7 @@ static std::array decode_color_to_float_array(const std::string color) return ret; } -void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) +void DSManagerForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) { if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) return; @@ -476,7 +380,7 @@ void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_re } } -void Control::render_menu() +void DSManagerForLayers::render_menu() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f) * m_scale); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_scale); @@ -538,7 +442,7 @@ void Control::render_menu() ImGui::PopStyleVar(3); } -bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) +bool DSManagerForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) { float scale = (float)wxGetApp().em_unit() / 10.0f; @@ -576,85 +480,76 @@ bool Control::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hover return ret; } -void Control::imgui_render(GUI::GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) +void DSManagerForGcode::imgui_render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) { if (!imgui_ctrl.IsShown()) return; m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); - const Size cnv_size = canvas.get_canvas_size(); - const int canvas_width = cnv_size.get_width(); - const int canvas_height = cnv_size.get_height(); + ImVec2 pos = ImVec2{std::max(LEFT_MARGIN, 0.2f * canvas_width), canvas_height - HORIZONTAL_SLIDER_WIDTH * m_scale}; + ImVec2 size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_WIDTH * m_scale); + + imgui_ctrl.Init(pos, size, m_scale); + if (imgui_ctrl.render()) + process_thumb_move(); +} + +void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) +{ + if (!imgui_ctrl.IsShown()) + return; + m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); + + const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; + const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; ImVec2 pos; ImVec2 size; - const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; - - if (imgui_ctrl.is_horizontal()) { - pos.x = std::max(LEFT_MARGIN, 0.2f * canvas_width); - pos.y = canvas_height - HORIZONTAL_SLIDER_WIDTH * m_scale; - size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_WIDTH * m_scale); - imgui_ctrl.ShowLabelOnMouseMove(false); - } - else { - const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; - - pos.x = canvas_width - VERTICAL_SLIDER_HEIGHT * m_scale - tick_icon_side; - pos.y = 1.f * action_btn_sz; - if (m_allow_editing) - pos.y += 2.f; - size = ImVec2(VERTICAL_SLIDER_HEIGHT * m_scale, canvas_height - 4.f * action_btn_sz); - imgui_ctrl.ShowLabelOnMouseMove(true); - } + pos.x = canvas_width - VERTICAL_SLIDER_HEIGHT * m_scale - tick_icon_side; + pos.y = 1.f * action_btn_sz; + if (m_allow_editing) + pos.y += 2.f; + size = ImVec2(VERTICAL_SLIDER_HEIGHT * m_scale, canvas_height - 4.f * action_btn_sz); + imgui_ctrl.ShowLabelOnMouseMove(true); imgui_ctrl.Init(pos, size, m_scale); - if (imgui_ctrl.render()) { - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); - } - if (imgui_ctrl.render()) { // request one more frame if value was changes with mouse wheel if (GImGui->IO.MouseWheel != 0.0f) wxGetApp().imgui()->set_requires_extra_frame(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); + process_thumb_move(); } // draw action buttons - if (!imgui_ctrl.is_horizontal()) { - const float groove_center_x = imgui_ctrl.GetGrooveRect().GetCenter().x; - - ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); - if (!m_ticks.empty() && m_allow_editing && - render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) - discard_all_thicks(); + const float groove_center_x = imgui_ctrl.GetGrooveRect().GetCenter().x; - btn_pos.y += 0.1f * action_btn_sz + size.y; - const bool is_one_layer = imgui_ctrl.IsCombineThumbs(); - if (render_button(is_one_layer ? ImGui::Lock : ImGui::Unlock, is_one_layer ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) - imgui_ctrl.CombineThumbs(!is_one_layer); + ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); - btn_pos.y += 1.2f * action_btn_sz; - if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) - show_cog_icon_context_menu(); + if (!m_ticks.empty() && m_allow_editing && + render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) + discard_all_thicks(); - if (m_draw_mode == dmSequentialFffPrint && imgui_ctrl.is_rclick_on_thumb()) { - std::string tooltip = _u8L("The sequential print is on.\n" - "It's impossible to apply any custom G-code for objects printing sequentually."); - ImGuiPureWrap::tooltip(tooltip, ImGui::GetFontSize() * 20.0f); - } - else if (m_allow_editing) - render_menu(); + btn_pos.y += 0.1f * action_btn_sz + size.y; + const bool is_one_layer = imgui_ctrl.IsCombineThumbs(); + if (render_button(is_one_layer ? ImGui::Lock : ImGui::Unlock, is_one_layer ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) + ChangeOneLayerLock(); + + btn_pos.y += 1.2f * action_btn_sz; + if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) + show_cog_icon_context_menu(); + + if (m_draw_mode == dmSequentialFffPrint && imgui_ctrl.is_rclick_on_thumb()) { + std::string tooltip = _u8L("The sequential print is on.\n" + "It's impossible to apply any custom G-code for objects printing sequentually."); + ImGuiPureWrap::tooltip(tooltip, ImGui::GetFontSize() * 20.0f); } + else if (m_allow_editing) + render_menu(); } -bool Control::is_wipe_tower_layer(int tick) const +bool DSManagerForLayers::is_wipe_tower_layer(int tick) const { if (!m_is_wipe_tower || tick >= (int)m_values.size()) return false; @@ -667,7 +562,7 @@ bool Control::is_wipe_tower_layer(int tick) const return false; } -static wxString short_and_splitted_time(const std::string& time) +static std::string short_and_splitted_time(const std::string& time) { // Parse the dhms time format. int days = 0; @@ -690,30 +585,30 @@ static wxString short_and_splitted_time(const std::string& time) auto get_s = [seconds](){ return GUI::format(_u8L("%1%s"), seconds); }; if (days > 0) - return format_wxstr("%1%%2%\n%3%", get_d(), get_h(), get_m()); + return GUI::format("%1%%2%\n%3%", get_d(), get_h(), get_m()); if (hours > 0) { if (hours < 10 && minutes < 10 && seconds < 10) - return format_wxstr("%1%%2%%3%", get_h(), get_m(), get_s()); + return GUI::format("%1%%2%%3%", get_h(), get_m(), get_s()); if (hours > 10 && minutes > 10 && seconds > 10) - return format_wxstr("%1%\n%2%\n%3%", get_h(), get_m(), get_s()); + return GUI::format("%1%\n%2%\n%3%", get_h(), get_m(), get_s()); if ((minutes < 10 && seconds > 10) || (minutes > 10 && seconds < 10)) - return format_wxstr("%1%\n%2%%3%", get_h(), get_m(), get_s()); - return format_wxstr("%1%%2%\n%3%", get_h(), get_m(), get_s()); + return GUI::format("%1%\n%2%%3%", get_h(), get_m(), get_s()); + return GUI::format("%1%%2%\n%3%", get_h(), get_m(), get_s()); } if (minutes > 0) { if (minutes > 10 && seconds > 10) - return format_wxstr("%1%\n%2%", get_m(), get_s()); - return format_wxstr("%1%%2%", get_m(), get_s()); + return GUI::format("%1%\n%2%", get_m(), get_s()); + return GUI::format("%1%%2%", get_m(), get_s()); } - return from_u8(get_s()); + return get_s(); } -wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const +std::string DSManagerForLayers::get_label(int pos, LabelType label_type/* = ltHeightWithLayer*/) const { - const size_t value = tick; + const size_t value = pos; if (m_values.empty()) - return wxString::Format("%lu", static_cast(value)); + return GUI::format("%1%", pos); if (value >= m_values.size()) return "ErrVal"; @@ -738,29 +633,25 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer return size_t(it - m_layers_values.begin()); }; - if (m_draw_mode == dmSequentialGCodeView) - return wxString::Format("%lu", static_cast(m_alternate_values[value])); - else { - if (label_type == ltEstimatedTime) { - if (m_is_wipe_tower) { - size_t layer_number = get_layer_number(value, label_type); - return (layer_number == size_t(-1) || layer_number == m_layers_times.size()) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); - } - return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; - } - wxString str = wxString::Format("%.*f", 2, m_values[value]); - if (label_type == ltHeight) - return str; - if (label_type == ltHeightWithLayer) { - size_t layer_number = m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); - return format_wxstr("%1%\n(%2%)", str, layer_number); + if (label_type == ltEstimatedTime) { + if (m_is_wipe_tower) { + size_t layer_number = get_layer_number(value, label_type); + return (layer_number == size_t(-1) || layer_number == m_layers_times.size()) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); } + return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; } + std::string str = GUI::format("%1$.2f", m_values[value]); + if (label_type == ltHeight) + return str; + if (label_type == ltHeightWithLayer) { + size_t layer_number = m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); + return GUI::format("%1%\n(%2%)", str, layer_number); + } - return wxEmptyString; + return ""; } -std::string Control::get_color_for_tool_change_tick(std::set::const_iterator it) const +std::string DSManagerForLayers::get_color_for_tool_change_tick(std::set::const_iterator it) const { const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; @@ -774,7 +665,7 @@ std::string Control::get_color_for_tool_change_tick(std::set::const_it return m_extruder_colors[current_extruder-1]; // return a color for a specific extruder from the colors list } -std::string Control::get_color_for_color_change_tick(std::set::const_iterator it) const +std::string DSManagerForLayers::get_color_for_color_change_tick(std::set::const_iterator it) const { const int def_extruder = std::max(1, m_only_extruder); auto it_n = it; @@ -796,23 +687,13 @@ std::string Control::get_color_for_color_change_tick(std::set::const_i return ""; } -bool Control::is_lower_thumb_editable() +void DSManagerForLayers::ChangeOneLayerLock() { - if (m_draw_mode == dmSequentialGCodeView) - return Slic3r::GUI::get_app_config()->get("seq_top_layer_only") == "0"; - return true; + imgui_ctrl.CombineThumbs(!imgui_ctrl.IsCombineThumbs()); + process_thumb_move(); } -void Control::ChangeOneLayerLock() -{ - imgui_ctrl.CombineThumbs(imgui_ctrl.IsCombineThumbs()); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -wxString Control::get_tooltip(int tick/*=-1*/) +wxString DSManagerForLayers::get_tooltip(int tick/*=-1*/) { if (m_focus == fiNone) return ""; @@ -822,15 +703,12 @@ wxString Control::get_tooltip(int tick/*=-1*/) return _L("Discard all custom changes"); if (m_focus == fiCogIcon) { - if (m_draw_mode == dmSequentialGCodeView) - return _L("Jump to move") + " (Shift + G)"; - else - return m_mode == MultiAsSingle ? - GUI::from_u8((boost::format(_u8L("Jump to height %s\n" - "Set ruler mode\n" - "or Set extruder sequence for the entire print")) % "(Shift + G)").str()) : - GUI::from_u8((boost::format(_u8L("Jump to height %s\n" - "or Set ruler mode")) % "(Shift + G)").str()); + return m_mode == MultiAsSingle ? + GUI::from_u8((boost::format(_u8L("Jump to height %s\n" + "Set ruler mode\n" + "or Set extruder sequence for the entire print")) % "(Shift + G)").str()) : + GUI::from_u8((boost::format(_u8L("Jump to height %s\n" + "or Set ruler mode")) % "(Shift + G)").str()); } if (m_focus == fiColorBand) return m_mode != SingleExtruder ? "" : @@ -943,7 +821,7 @@ wxString Control::get_tooltip(int tick/*=-1*/) } -void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +void DSManagerForLayers::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { @@ -974,7 +852,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current } } -void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +void DSManagerForLayers::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { @@ -1002,86 +880,16 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren } } -// "condition" have to be true for: -// - value increase (if wxSL_VERTICAL) -// - value decrease (if wxSL_HORIZONTAL) -void Control::move_current_thumb(const bool condition) -{ -// m_is_one_layer = wxGetKeyState(WXK_CONTROL); - int delta = condition ? -1 : 1; - if (imgui_ctrl.is_horizontal()) - delta *= -1; - - // accelerators - int accelerator = 0; - if (wxGetKeyState(WXK_SHIFT)) - accelerator += 5; - if (wxGetKeyState(WXK_CONTROL)) - accelerator += 5; - if (accelerator > 0) - delta *= accelerator; - - imgui_ctrl.MoveActiveThumb(delta); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void Control::UseDefaultColors(bool def_colors_on) +void DSManagerForLayers::UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } -void Control::OnKeyDown(wxKeyEvent &event) -{ - const int key = event.GetKeyCode(); - if (m_draw_mode != dmSequentialGCodeView && key == WXK_NUMPAD_ADD) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of add_tick() twice. - // To avoid this case we should suppress second add_tick() call. - m_ticks.suppress_plus(true); - add_current_tick(true); - } - else if (m_draw_mode != dmSequentialGCodeView && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK)) { - // OnChar() is called immediately after OnKeyDown(), which can cause call of delete_tick() twice. - // To avoid this case we should suppress second delete_tick() call. - m_ticks.suppress_minus(true); - delete_current_tick(); - } - else if (m_draw_mode != dmSequentialGCodeView && event.GetKeyCode() == WXK_SHIFT) - UseDefaultColors(false); - else if (imgui_ctrl.is_horizontal()) { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - - event.Skip(); // !Needed to have EVT_CHAR generated as well -} - -void Control::OnChar(wxKeyEvent& event) -{ - const int key = event.GetKeyCode(); - if (m_draw_mode != dmSequentialGCodeView) { - if (key == '+' && !m_ticks.suppressed_plus()) { - add_current_tick(true); - m_ticks.suppress_plus(false); - } - else if (key == '-' && !m_ticks.suppressed_minus()) { - delete_current_tick(); - m_ticks.suppress_minus(false); - } - } - if (key == 'G') - jump_to_value(); -} - // Get active extruders for tick. // Means one current extruder for not existing tick OR // 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick) // Use those values to disable selection of active extruders -std::array Control::get_active_extruders_for_tick(int tick) const +std::array DSManagerForLayers::get_active_extruders_for_tick(int tick) const { int default_initial_extruder = m_mode == MultiAsSingle ? std::max(1, m_only_extruder) : 1; std::array extruders = { default_initial_extruder, -1 }; @@ -1164,7 +972,7 @@ std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extru return used_extruders; } -void Control::show_cog_icon_context_menu() +void DSManagerForLayers::show_cog_icon_context_menu() { wxMenu menu; @@ -1217,12 +1025,12 @@ bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t return detected; } -void Control::auto_color_change() +void DSManagerForLayers::auto_color_change() { if (!m_ticks.empty()) { wxString msg_text = _L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" + _L("This action is not revertible.\nDo you want to proceed?"); - GUI::WarningDialog dialog(m_parent, msg_text, _L("Warning"), wxYES | wxNO); + GUI::WarningDialog dialog(nullptr, msg_text, _L("Warning"), wxYES | wxNO); if (dialog.ShowModal() == wxID_NO) return; m_ticks.ticks.clear(); @@ -1364,9 +1172,9 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height) static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode) { - wxString msg_text = (mode == dmSequentialGCodeView) ? _L("Enter the move you want to jump to") + ":" : _L("Enter the height you want to jump to") + ":"; - wxString msg_header = (mode == dmSequentialGCodeView) ? _L("Jump to move") : _L("Jump to height"); - wxString msg_in = GUI::double_to_string(active_value); + wxString msg_text = _L("Enter the height you want to jump to") + ":"; + wxString msg_header = _L("Jump to height"); + wxString msg_in = GUI::double_to_string(active_value); // get custom gcode wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle); @@ -1379,7 +1187,7 @@ static double get_value_to_jump(double active_value, double min_z, double max_z, return dlg.GetValue().ToDouble(&value) ? value : -1.0; } -void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) +void DSManagerForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { const int tick = imgui_ctrl.GetActiveValue(); @@ -1413,7 +1221,7 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/) post_ticks_changed_event(type); } -void Control::add_current_tick(bool call_from_keyboard /*= false*/) +void DSManagerForLayers::add_current_tick(bool call_from_keyboard /*= false*/) { const int tick = imgui_ctrl.GetActiveValue(); auto it = m_ticks.ticks.find(TickCode{ tick }); @@ -1455,7 +1263,7 @@ void Control::add_current_tick(bool call_from_keyboard /*= false*/) */ } -void Control::delete_current_tick() +void DSManagerForLayers::delete_current_tick() { auto it = m_ticks.ticks.find(TickCode{ imgui_ctrl.GetActiveValue()}); if (it == m_ticks.ticks.end() || @@ -1467,7 +1275,7 @@ void Control::delete_current_tick() post_ticks_changed_event(type); } -void Control::edit_tick(int tick/* = -1*/) +void DSManagerForLayers::edit_tick(int tick/* = -1*/) { if (tick < 0) tick = imgui_ctrl.GetActiveValue(); @@ -1482,14 +1290,8 @@ void Control::edit_tick(int tick/* = -1*/) post_ticks_changed_event(type); } -// switch on/off one layer mode -void Control::switch_one_layer_mode() -{ - imgui_ctrl.CombineThumbs(!imgui_ctrl.IsCombineThumbs()); -} - // discard all custom changes on DoubleSlider -void Control::discard_all_thicks() +void DSManagerForLayers::discard_all_thicks() { m_ticks.ticks.clear(); imgui_ctrl.ResetValues(); @@ -1497,7 +1299,7 @@ void Control::discard_all_thicks() post_ticks_changed_event(); } -void Control::edit_extruder_sequence() +void DSManagerForLayers::edit_extruder_sequence() { if (!check_ticks_changed_event(ToolChange)) return; @@ -1558,7 +1360,7 @@ void Control::edit_extruder_sequence() post_ticks_changed_event(ToolChange); } -void Control::jump_to_value() +void DSManagerForLayers::jump_to_value() { double value = get_value_to_jump(m_values[imgui_ctrl.GetActiveValue()], m_values[imgui_ctrl.GetMinValue()], m_values[imgui_ctrl.GetMaxValue()], m_draw_mode); @@ -1573,14 +1375,13 @@ void Control::jump_to_value() SetLowerValue(tick_value); } -void Control::post_ticks_changed_event(Type type /*= Custom*/) +void DSManagerForLayers::post_ticks_changed_event(Type type /*= Custom*/) { // m_force_mode_apply = type != ToolChange; // It looks like this condition is no needed now. Leave it for the testing - - wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + process_ticks_changed(); } -bool Control::check_ticks_changed_event(Type type) +bool DSManagerForLayers::check_ticks_changed_event(Type type) { if ( m_ticks.mode == m_mode || (type != ColorChange && type != ToolChange) || @@ -1602,7 +1403,7 @@ bool Control::check_ticks_changed_event(Type type) _L("Are you sure you want to continue?"); //wxMessageDialog msg(this, message, _L("Notice"), wxYES_NO); - GUI::MessageDialog msg(this, message, _L("Notice"), wxYES_NO); + GUI::MessageDialog msg(nullptr, message, _L("Notice"), wxYES_NO); if (msg.ShowModal() == wxID_YES) { m_ticks.erase_all_ticks_with_code(ColorChange); post_ticks_changed_event(ColorChange); @@ -1622,7 +1423,7 @@ bool Control::check_ticks_changed_event(Type type) _L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" + _L("Are you sure you want to continue?") ) ; - GUI::MessageDialog msg(this, message, _L("Notice"), wxYES_NO | (m_mode == SingleExtruder ? wxCANCEL : 0)); + GUI::MessageDialog msg(nullptr, message, _L("Notice"), wxYES_NO | (m_mode == SingleExtruder ? wxCANCEL : 0)); const int answer = msg.ShowModal(); if (answer == wxID_YES) { m_ticks.erase_all_ticks_with_code(ToolChange); @@ -1638,6 +1439,25 @@ bool Control::check_ticks_changed_event(Type type) return true; } +DSManagerForLayers::DSManagerForLayers( int lowerValue, + int higherValue, + int minValue, + int maxValue, + bool allow_editing/* = true*/) : + m_allow_editing(allow_editing) +{ +#ifdef __WXOSX__ + is_osx = true; +#endif //__WXOSX__ + Init(lowerValue, higherValue, minValue, maxValue, "layers_slider", false); + + imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); + imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); + + m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); + m_ticks.set_extruder_colors(&m_extruder_colors); +} + std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder) { auto opposite_one_color = [](const std::string& color) { diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 74e4f2e08b..5a4c08d701 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -6,16 +6,8 @@ #define slic3r_GUI_DoubleSlider_hpp_ #include "libslic3r/CustomGCode.hpp" -#include "wxExtensions.hpp" -#include "GLCanvas3D.hpp" - #include "ImGuiDoubleSlider.hpp" -#include -#include -#include -#include - #include #include @@ -45,9 +37,6 @@ bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t // and return true when detection have to be desturbed std::function break_condition); -// custom message the slider sends to its parent to notify a tick-change: -wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent); - enum FocusedItem { fiNone, @@ -90,7 +79,6 @@ enum DrawMode dmRegular, dmSlaPrint, dmSequentialFffPrint, - dmSequentialGCodeView, }; enum LabelType @@ -204,65 +192,164 @@ struct ExtrudersSequence } }; -class Control : public wxControl +template +class DSManager_t { public: - Control( - wxWindow *parent, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - long style = wxSL_VERTICAL, - const wxString& name = wxEmptyString); - ~Control() {} + + void Init( int lowerValue, + int higherValue, + int minValue, + int maxValue, + const std::string& name, + bool is_horizontal) + { + imgui_ctrl = ImGuiControl( lowerValue, higherValue, + minValue, maxValue, + is_horizontal ? 0 : ImGuiSliderFlags_Vertical, + name, !is_horizontal); + + imgui_ctrl.set_get_label_cb([this](int pos) {return get_label(pos); }); + }; + + DSManager_t() {} + DSManager_t(int lowerValue, + int higherValue, + int minValue, + int maxValue, + const std::string& name, + bool is_horizontal) + { + Init (lowerValue, higherValue, minValue, maxValue, name, is_horizontal); + } + ~DSManager_t() {} int GetMinValue() const { return imgui_ctrl.GetMinValue(); } int GetMaxValue() const { return imgui_ctrl.GetMaxValue(); } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[GetMinValue()]; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[GetMaxValue()]; } int GetLowerValue() const { return imgui_ctrl.GetLowerValue(); } int GetHigherValue()const { return imgui_ctrl.GetHigherValue(); } - double GetLowerValueD() { return get_double_value(ssLower); } - double GetHigherValueD() { return get_double_value(ssHigher); } + + ValType GetMinValueD() { return m_values.empty() ? static_cast(0) : m_values[GetMinValue()]; } + ValType GetMaxValueD() { return m_values.empty() ? static_cast(0) : m_values[GetMaxValue()]; } + ValType GetLowerValueD() { return m_values.empty() ? static_cast(0) : m_values[GetLowerValue()];} + ValType GetHigherValueD() { return m_values.empty() ? static_cast(0) : m_values[GetHigherValue()]; } // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetLowerValue (const int lower_val); - void SetHigherValue(const int higher_val); - void SetSelectionSpan(const int lower_val, const int higher_val); + void SetLowerValue(const int lower_val) { + imgui_ctrl.SetLowerValue(lower_val); + process_thumb_move(); + } + void SetHigherValue(const int higher_val) { + imgui_ctrl.SetHigherValue(higher_val); + process_thumb_move(); + } + void SetSelectionSpan(const int lower_val, const int higher_val) { + imgui_ctrl.SetSelectionSpan(lower_val, higher_val); + process_thumb_move(); + } + void SetMaxValue(const int max_value) { + imgui_ctrl.SetMaxValue(max_value); + process_thumb_move(); + } - void SetMaxValue(const int max_value); - void SetSliderValues(const std::vector& values); - void ChangeOneLayerLock(); - void SetSliderAlternateValues(const std::vector& values) { m_alternate_values = values; } - - Info GetTicksValues() const; - void SetTicksValues(const Info &custom_gcode_per_print_z); - void SetLayersTimes(const std::vector& layers_times, float total_time); - void SetLayersTimes(const std::vector& layers_times); - - void SetDrawMode(bool is_sla_print, bool is_sequential_print); - void SetDrawMode(DrawMode mode) { m_draw_mode = mode; } - - void SetManipulationMode(Mode mode) { m_mode = mode; } - Mode GetManipulationMode() const { return m_mode; } - void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); - void SetExtruderColors(const std::vector& extruder_colors); - - bool IsNewPrint(); - - void set_render_as_disabled(bool value) { m_render_as_disabled = value; } - bool is_rendering_as_disabled() const { return m_render_as_disabled; } + void SetSliderValues(const std::vector& values) { m_values = values; } + // values used to show thumb labels + void SetSliderAlternateValues(const std::vector& values) { m_alternate_values = values; } bool is_lower_at_min() const { return imgui_ctrl.is_lower_at_min(); } bool is_higher_at_max() const { return imgui_ctrl.is_higher_at_max(); } - void UseDefaultColors(bool def_colors_on); - void OnKeyDown(wxKeyEvent &event); - void OnChar(wxKeyEvent &event); + void Show(bool show = true) { imgui_ctrl.Show(show); } + void Hide() { Show(false); } - bool Show(bool show = true) override { imgui_ctrl.Show(show); return true; } - bool Hide() { return Show(false); } + virtual void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) = 0; + + void set_callback_on_thumb_move(std::function cb) { m_cb_thumb_move = cb; }; + + void move_current_thumb(const int delta) + { + imgui_ctrl.MoveActiveThumb(delta); + process_thumb_move(); + } + +protected: + + std::vector m_values; + std::vector m_alternate_values; + + GUI::ImGuiControl imgui_ctrl; + float m_scale{ 1.f }; + + std::string get_label(int pos) const { + if (m_values.empty()) + return GUI::format("%1%", pos); + if (pos >= m_values.size()) + return "ErrVal"; + return GUI::format("%1%", static_cast(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos])); + } + + void process_thumb_move() { + if (m_cb_thumb_move) + m_cb_thumb_move(); + } + +private: + + std::function m_cb_thumb_move{ nullptr }; +}; + + +class DSManagerForGcode : public DSManager_t +{ +public: + DSManagerForGcode() : DSManager_t() {} + DSManagerForGcode( int lowerValue, + int higherValue, + int minValue, + int maxValue) + { + Init(lowerValue, higherValue, minValue, maxValue, "moves_slider", true); + } + ~DSManagerForGcode() {} + + void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + + void set_render_as_disabled(bool value) { m_render_as_disabled = value; } + bool is_rendering_as_disabled() const { return m_render_as_disabled; } + +private: + + bool m_render_as_disabled{ false }; +}; + + + +class DSManagerForLayers : public DSManager_t +{ +public: + DSManagerForLayers() : DSManager_t() {} + DSManagerForLayers( int lowerValue, + int higherValue, + int minValue, + int maxValue, + bool allow_editing = true); + ~DSManagerForLayers() {} + + void ChangeOneLayerLock(); + + Info GetTicksValues() const; + void SetTicksValues(const Info& custom_gcode_per_print_z); + void SetLayersTimes(const std::vector& layers_times, float total_time); + void SetLayersTimes(const std::vector& layers_times); + + void SetDrawMode(bool is_sla_print, bool is_sequential_print); + + void SetManipulationMode(Mode mode) { m_mode = mode; } + Mode GetManipulationMode() const { return m_mode; } + void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); + void SetExtruderColors(const std::vector& extruder_colors); + + void UseDefaultColors(bool def_colors_on); void add_code_as_tick(Type type, int selected_extruder = -1); // add default action for tick, when press "+" @@ -270,31 +357,36 @@ public: // delete current tick, when press "-" void delete_current_tick(); void edit_tick(int tick = -1); - void switch_one_layer_mode(); void discard_all_thicks(); void edit_extruder_sequence(); - void jump_to_value(); void enable_action_icon(bool enable) { m_enable_action_icon = enable; } void show_cog_icon_context_menu(); void auto_color_change(); + void jump_to_value(); - void imgui_render(GUI::GLCanvas3D& canvas, float extra_scale = 1.f); + void set_callback_on_ticks_changed(std::function cb) { m_cb_ticks_changed = cb; }; + + void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + + bool IsNewPrint(const std::string& print_obj_idxs); private: - bool is_lower_thumb_editable(); - void move_current_thumb(const bool condition); bool is_wipe_tower_layer(int tick) const; - wxString get_label(int tick, LabelType label_type = ltHeightWithLayer) const; + std::string get_label(int tick, LabelType label_type = ltHeightWithLayer) const; - double get_double_value(const SelectedSlider& selection); int get_tick_from_value(double value, bool force_lower_bound = false); wxString get_tooltip(int tick = -1); std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + void process_ticks_changed() { + if (m_cb_ticks_changed) + m_cb_ticks_changed(); + } + // Get active extruders for tick. // Means one current extruder for not existing tick OR // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) @@ -304,19 +396,17 @@ private: void post_ticks_changed_event(Type type = Custom); bool check_ticks_changed_event(Type type); - void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false); + void append_change_extruder_menu_item(wxMenu*, bool switch_current_code = false); void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); - bool is_osx { false }; - - bool m_render_as_disabled{ false }; + bool is_osx{ false }; bool m_allow_editing{ true }; bool m_force_mode_apply = true; bool m_enable_action_icon = true; bool m_is_wipe_tower = false; //This flag indicates that there is multiple extruder print with wipe tower - DrawMode m_draw_mode = dmRegular; + DrawMode m_draw_mode { dmRegular }; Mode m_mode = SingleExtruder; int m_only_extruder = -1; @@ -326,19 +416,18 @@ private: bool m_show_estimated_times{ false }; - std::vector m_values; TickCodeInfo m_ticks; std::vector m_layers_times; std::vector m_layers_values; std::vector m_extruder_colors; - std::string m_print_obj_idxs; - - std::vector m_alternate_values; ExtrudersSequence m_extruders_sequence; + std::function m_cb_ticks_changed{ nullptr }; + + std::string m_print_obj_idxs; + // ImGuiDS - float m_scale{ 1.f }; bool m_can_change_color{ true }; void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); @@ -346,8 +435,6 @@ private: void render_menu(); bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1); void update_callbacks(); - - GUI::ImGuiControl imgui_ctrl; }; } // DoubleSlider; diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp index 61cd057425..37046834af 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.hpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -7,6 +7,7 @@ #include "GUI_Utils.hpp" #include "DoubleSlider.hpp" +#include "wxExtensions.hpp" class wxTextCtrl; class wxFlexGridSizer; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 28cf4f0d7b..7c111d63c5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -77,7 +77,6 @@ #include #include #include -#include "DoubleSlider.hpp" #include #include @@ -1019,9 +1018,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent); @@ -2828,14 +2825,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case '6': { select_view("right"); break; } case '+': { if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt)); + post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt)); else post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } case '-': { if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt)); + post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt)); else post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; @@ -2853,7 +2850,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'g': { if ((evt.GetModifiers() & shiftMask) != 0) { if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_JUMP_TO, evt)); + post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt)); } break; } @@ -3138,7 +3135,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) keyCode == WXK_UP || keyCode == WXK_DOWN) { if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_SLIDERS, evt)); + post_event(wxKeyEvent(EVT_GLCANVAS_SLIDERS_MANIPULATION, evt)); } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 01d10032ba..ccc29d4b84 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -176,9 +176,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_SLIDERS_MANIPULATION, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 35b1e3b284..e3a9aadfef 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -231,14 +231,11 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) m_canvas->show_legend(true); m_canvas->enable_dynamic_background(true); - create_layers_slider(); + create_sliders(); m_left_sizer = new wxBoxSizer(wxVERTICAL); m_left_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0); - m_moves_slider = new DoubleSlider::Control(this, 0, 0, 0, 100, wxSL_HORIZONTAL, "moves_slider"); - m_moves_slider->SetDrawMode(DoubleSlider::dmSequentialGCodeView); - wxBoxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL); main_sizer->Add(m_left_sizer, 1, wxALL | wxEXPAND, 0); @@ -260,6 +257,11 @@ Preview::~Preview() if (m_canvas_widget != nullptr) delete m_canvas_widget; + + if (m_layers_slider) + delete m_layers_slider; + if (m_moves_slider) + delete m_moves_slider; } void Preview::set_as_dirty() @@ -321,42 +323,24 @@ void Preview::msw_rescale() void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) { + const Size cnv_size = canvas.get_canvas_size(); + const int canvas_width = cnv_size.get_width(); + const int canvas_height = cnv_size.get_height(); + if (m_layers_slider) - m_layers_slider->imgui_render(canvas, extra_scale); + m_layers_slider->imgui_render(canvas_width, canvas_height, extra_scale); if (m_moves_slider) - m_moves_slider->imgui_render(canvas, extra_scale); -} - -void Preview::jump_layers_slider(wxKeyEvent& evt) -{ - if (m_layers_slider) m_layers_slider->OnChar(evt); -} - -void Preview::move_layers_slider(wxKeyEvent& evt) -{ - if (m_layers_slider != nullptr) m_layers_slider->OnKeyDown(evt); -} - -void Preview::edit_layers_slider(wxKeyEvent& evt) -{ - if (m_layers_slider != nullptr) m_layers_slider->OnChar(evt); + m_moves_slider->imgui_render(canvas_width, canvas_height, extra_scale); } void Preview::bind_event_handlers() { Bind(wxEVT_SIZE, &Preview::on_size, this); - m_moves_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); } void Preview::unbind_event_handlers() { Unbind(wxEVT_SIZE, &Preview::on_size, this); - m_moves_slider->Unbind(wxEVT_SCROLL_CHANGED, &Preview::on_moves_slider_scroll_changed, this); -} - -void Preview::move_moves_slider(wxKeyEvent& evt) -{ - if (m_moves_slider != nullptr) m_moves_slider->OnKeyDown(evt); } void Preview::hide_layers_slider() @@ -370,26 +354,20 @@ void Preview::on_size(wxSizeEvent& evt) Refresh(); } -void Preview::create_layers_slider() +void Preview::create_sliders() { - m_layers_slider = new DoubleSlider::Control(this,0, 0, 0, 100, wxVERTICAL, "layers_slider"); + // Layers Slider + + m_layers_slider = new DoubleSlider::DSManagerForLayers(0, 0, 0, 100, wxGetApp().is_editor()); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, - wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); + wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); m_layers_slider->enable_action_icon(wxGetApp().is_editor()); - // sizer, m_canvas_widget - m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this); - m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { - if (event.GetKeyCode() == WXK_SHIFT) - m_layers_slider->UseDefaultColors(true); - event.Skip(); - }); + m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } ); - m_layers_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_layers_slider_scroll_changed, this); - - Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { + m_layers_slider->set_callback_on_ticks_changed( [this]() -> void { Model& model = wxGetApp().plater()->model(); model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues(); m_schedule_background_process(); @@ -397,6 +375,16 @@ void Preview::create_layers_slider() m_keep_current_preview_type = false; reload_print(); }); + + // Move Gcode Slider + + m_moves_slider = new DoubleSlider::DSManagerForGcode(0, 0, 0, 100); + + m_moves_slider->set_callback_on_thumb_move([this]() ->void { Preview::on_moves_slider_scroll_changed(); }); + + // m_canvas_widget + m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_sliders_from_canvas, this); + m_canvas_widget->Bind(EVT_GLCANVAS_SLIDERS_MANIPULATION, &Preview::update_sliders_from_canvas, this); } // Find an index of a value in a sorted vector, which is in . @@ -510,10 +498,20 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee break; } + auto get_print_obj_idxs = [plater]() ->std::string { + if (plater->printer_technology() == ptSLA) + return "sla"; + const Print& print = GUI::wxGetApp().plater()->fff_print(); + std::string idxs; + for (auto object : print.objects()) + idxs += std::to_string(object->id().id) + "_"; + return idxs; + }; + // Suggest the auto color change, if model looks like sign if (!color_change_already_exists && wxGetApp().app_config->get_bool("allow_auto_color_change") && - m_layers_slider->IsNewPrint()) + m_layers_slider->IsNewPrint(get_print_obj_idxs())) { const Print& print = wxGetApp().plater()->fff_print(); @@ -631,16 +629,39 @@ void Preview::reset_layers_slider() m_layers_slider->SetLowerValue(0); } -void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) +void Preview::update_sliders_from_canvas(wxKeyEvent& event) { - if (event.HasModifiers()) { + const auto key = event.GetKeyCode(); + + if (key == WXK_NUMPAD_ADD || key == '+') + m_layers_slider->add_current_tick(true); + else if (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK || key == '-') + m_layers_slider->delete_current_tick(); + else if (key == 'G' || key == 'g') + m_layers_slider->jump_to_value(); + else if (key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) { + int delta = 1; + // accelerators + int accelerator = 0; + if (wxGetKeyState(WXK_SHIFT)) + accelerator += 5; + if (wxGetKeyState(WXK_CONTROL)) + accelerator += 5; + if (accelerator > 0) + delta *= accelerator; + + if (key == WXK_LEFT || key == WXK_RIGHT) + m_moves_slider->move_current_thumb(delta * (key == WXK_LEFT ? 1 : -1)); + else if (key == WXK_UP || key == WXK_DOWN) + m_layers_slider->move_current_thumb(delta * (key == WXK_DOWN ? 1 : -1)); + } + + else if (event.HasModifiers()) { event.Skip(); return; } - const auto key = event.GetKeyCode(); - - if (key == 'S' || key == 'W') { + else if (key == 'S' || key == 'W') { const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1; m_layers_slider->SetHigherValue(new_pos); } @@ -650,8 +671,6 @@ void Preview::update_layers_slider_from_canvas(wxKeyEvent& event) } else if (key == 'X') m_layers_slider->ChangeOneLayerLock(); - else if (key == WXK_SHIFT) - m_layers_slider->UseDefaultColors(false); else event.Skip(); } @@ -669,9 +688,9 @@ void Preview::update_moves_slider(std::optional visible_range_min, std::opt std::optional{ m_canvas->get_gcode_vertex_at(*visible_range_max).gcode_id } : std::nullopt; const size_t range_size = range[1] - range[0] + 1; - std::vector values; + std::vector values; values.reserve(range_size); - std::vector alternate_values; + std::vector alternate_values; alternate_values.reserve(range_size); std::optional visible_range_min_id; @@ -684,7 +703,7 @@ void Preview::update_moves_slider(std::optional visible_range_min, std::opt if (i > range[0]) { // skip consecutive moves with same gcode id (resulting from processing G2 and G3 lines) if (last_gcode_id == gcode_id) { - values.back() = static_cast(i + 1); + values.back() = i + 1; skip = true; } else @@ -692,11 +711,11 @@ void Preview::update_moves_slider(std::optional visible_range_min, std::opt } if (!skip) { - values.emplace_back(static_cast(i + 1)); - alternate_values.emplace_back(static_cast(gcode_id)); - if (gcode_id_min.has_value() && alternate_values.back() == static_cast(*gcode_id_min)) + values.emplace_back(i + 1); + alternate_values.emplace_back(gcode_id); + if (gcode_id_min.has_value() && alternate_values.back() == *gcode_id_min) visible_range_min_id = counter; - else if (gcode_id_max.has_value() && alternate_values.back() == static_cast(*gcode_id_max)) + else if (gcode_id_max.has_value() && alternate_values.back() == *gcode_id_max) visible_range_max_id = counter; ++counter; } @@ -716,7 +735,6 @@ void Preview::enable_moves_slider(bool enable) bool render_as_disabled = !enable; if (m_moves_slider != nullptr && m_moves_slider->is_rendering_as_disabled() != render_as_disabled) { m_moves_slider->set_render_as_disabled(render_as_disabled); - m_moves_slider->Refresh(); } } @@ -865,7 +883,7 @@ void Preview::load_print_as_sla() } } -void Preview::on_layers_slider_scroll_changed(wxCommandEvent& event) +void Preview::on_layers_slider_scroll_changed() { if (IsShown()) { PrinterTechnology tech = m_process->current_printer_technology(); @@ -882,7 +900,7 @@ void Preview::on_layers_slider_scroll_changed(wxCommandEvent& event) } } -void Preview::on_moves_slider_scroll_changed(wxCommandEvent& event) +void Preview::on_moves_slider_scroll_changed() { m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD() - 1.0), static_cast(m_moves_slider->GetHigherValueD() - 1.0)); m_canvas->set_as_dirty(); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 90484a8a0b..452185fbe1 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -28,7 +28,8 @@ class BackgroundSlicingProcess; class Model; namespace DoubleSlider { - class Control; + class DSManagerForGcode; + class DSManagerForLayers; }; namespace GUI { @@ -95,8 +96,8 @@ class Preview : public wxPanel bool m_loaded { false }; - DoubleSlider::Control* m_layers_slider{ nullptr }; - DoubleSlider::Control* m_moves_slider{ nullptr }; + DoubleSlider::DSManagerForLayers* m_layers_slider{ nullptr }; + DoubleSlider::DSManagerForGcode* m_moves_slider { nullptr }; public: enum class OptionType : unsigned int @@ -133,9 +134,6 @@ public: void reload_print(); void msw_rescale(); - void jump_layers_slider(wxKeyEvent& evt); - void move_layers_slider(wxKeyEvent& evt); - void edit_layers_slider(wxKeyEvent& evt); void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); @@ -143,7 +141,6 @@ public: void update_moves_slider(std::optional visible_range_min = std::nullopt, std::optional visible_range_max = std::nullopt); void enable_moves_slider(bool enable); - void move_moves_slider(wxKeyEvent& evt); void hide_layers_slider(); void set_keep_current_preview_type(bool value) { m_keep_current_preview_type = value; } @@ -159,20 +156,20 @@ private: void on_size(wxSizeEvent& evt); // Create/Update/Reset double slider on 3dPreview - void create_layers_slider(); + void create_sliders(); void check_layers_slider_values(std::vector& ticks_from_model, const std::vector& layers_z); void reset_layers_slider(); void update_layers_slider(const std::vector& layers_z, bool keep_z_range = false); void update_layers_slider_mode(); // update vertical DoubleSlider after keyDown in canvas - void update_layers_slider_from_canvas(wxKeyEvent& event); + void update_sliders_from_canvas(wxKeyEvent& event); void load_print_as_fff(bool keep_z_range = false); void load_print_as_sla(); - void on_layers_slider_scroll_changed(wxCommandEvent& event); - void on_moves_slider_scroll_changed(wxCommandEvent& event); + void on_layers_slider_scroll_changed(); + void on_moves_slider_scroll_changed(); }; } // namespace GUI diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index ab65590d80..9befb33b87 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -120,14 +120,10 @@ ImGuiControl::ImGuiControl( int lowerValue, int higherValue, int minValue, int maxValue, - ImVec2 pos, - ImVec2 size, ImGuiSliderFlags flags, std::string name, bool use_lower_thumb) : m_selection(ssUndef), - m_pos(pos), - m_size(size), m_name(name), m_lower_value(lowerValue), m_higher_value (higherValue), diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 425a33d8c7..b2f2263707 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -32,13 +32,6 @@ enum SelectedSlider { ssHigher }; -enum LabelType -{ - ltHeightWithLayer, - ltHeight, - ltEstimatedTime, -}; - class ImGuiControl { public: @@ -46,8 +39,6 @@ public: int higherValue, int minValue, int maxValue, - ImVec2 pos, - ImVec2 size, ImGuiSliderFlags flags = ImGuiSliderFlags_None, std::string name = "d_slider", bool use_lower_thumb = true); @@ -79,12 +70,12 @@ public: m_draw_opts.scale = scale; } - void Show(bool show) { m_is_shown = show; } - void Hide() { m_is_shown = false; } - bool IsShown() const { return m_is_shown; } - void MoveActiveThumb(int delta); - bool IsCombineThumbs() const { return m_combine_thumbs; } - bool IsActiveHigherThumb() const { return m_selection == ssHigher; } + void Show(bool show) { m_is_shown = show; } + void Hide() { m_is_shown = false; } + bool IsShown() const { return m_is_shown; } + bool IsCombineThumbs() const { return m_combine_thumbs; } + bool IsActiveHigherThumb() const { return m_selection == ssHigher; } + void MoveActiveThumb(int delta); void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); } @@ -95,11 +86,7 @@ public: bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } bool is_rclick_on_thumb() const { return m_rclick_on_selected_thumb; } - void correct_lower_value(); - void correct_higher_value(); - bool render(); - void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); std::string get_label(int pos) const; @@ -108,6 +95,8 @@ public: void set_draw_scroll_line_cb(std::function cb) { m_cb_draw_scroll_line = cb; } void set_extra_draw_cb(std::function cb) { m_cb_extra_draw = cb; } +private: + struct DrawOptions { float scale { 1.f }; // used for Retina on osx @@ -134,8 +123,6 @@ public: ImRect lower_thumb; }; -private: - SelectedSlider m_selection; ImVec2 m_pos; ImVec2 m_size; @@ -143,31 +130,34 @@ private: ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None }; bool m_is_shown{ true }; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - int m_mouse_pos_value; + int m_min_value; + int m_max_value; + int m_lower_value; + int m_higher_value; + int m_mouse_pos_value; - bool m_rclick_on_selected_thumb{ false }; + bool m_rclick_on_selected_thumb{ false }; - bool m_draw_lower_thumb{ true }; - bool m_combine_thumbs { false }; - bool m_show_move_label{ false }; + bool m_draw_lower_thumb{ true }; + bool m_combine_thumbs { false }; + bool m_show_move_label { false }; - DrawOptions m_draw_opts; - Regions m_regions; + DrawOptions m_draw_opts; + Regions m_regions; std::function m_cb_get_label { nullptr }; std::function m_cb_get_label_on_move { nullptr }; std::function m_cb_draw_scroll_line { nullptr }; std::function m_cb_extra_draw { nullptr }; - void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); + void correct_lower_value(); + void correct_higher_value(); std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } + void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); void check_and_correct_thumbs(int* higher_value, int* lower_value); + void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); void draw_background(const ImRect& slideable_region); void draw_label(std::string label, const ImRect& thumb); void draw_thumb(const ImVec2& center, bool mark = false); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index da95c696c2..73f3d7af20 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -736,12 +736,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); }); } - preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_JUMP_TO, [this](wxKeyEvent& evt) { preview->jump_layers_slider(evt); }); - preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_SLIDERS, [this](wxKeyEvent& evt) { - preview->move_layers_slider(evt); - preview->move_moves_slider(evt); - }); - preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_layers_slider(evt); }); + if (wxGetApp().is_gcode_viewer()) preview->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->q->reload_gcode_from_disk(); }); From 6b83f135c988076f98abd5de38168c0c1733de34 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 25 Mar 2024 14:49:30 +0100 Subject: [PATCH 08/13] ImguiDoubleSlider: WIP: Slider Manager is moved to ImGuiDoubleSlider and some values and methods are renamed + "DoubleSlider: file is split to DoubleSliderForGcode and DoubleSliderForLayers in separate files + Suppress to add/delete ticks on update layers_slider from canvas when an application is a GCodeViewer + epsilon() is moved from DoubleSlider name space to CustomGCode --- resources/localization/list.txt | 2 +- src/libslic3r/CustomGCode.hpp | 5 + src/slic3r/CMakeLists.txt | 6 +- src/slic3r/GUI/DoubleSliderForGcode.cpp | 33 ++ src/slic3r/GUI/DoubleSliderForGcode.hpp | 42 ++ ...leSlider.cpp => DoubleSliderForLayers.cpp} | 446 +++++++++--------- ...leSlider.hpp => DoubleSliderForLayers.hpp} | 265 +++-------- src/slic3r/GUI/ExtruderSequenceDialog.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 6 +- src/slic3r/GUI/GUI_Preview.cpp | 79 ++-- src/slic3r/GUI/GUI_Preview.hpp | 8 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 171 +++---- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 173 +++++-- 13 files changed, 637 insertions(+), 601 deletions(-) create mode 100644 src/slic3r/GUI/DoubleSliderForGcode.cpp create mode 100644 src/slic3r/GUI/DoubleSliderForGcode.hpp rename src/slic3r/GUI/{DoubleSlider.cpp => DoubleSliderForLayers.cpp} (84%) rename src/slic3r/GUI/{DoubleSlider.hpp => DoubleSliderForLayers.hpp} (51%) diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 8acf52612d..9d8429f41c 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -30,7 +30,7 @@ src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp src/slic3r/GUI/ConfigWizard.cpp src/slic3r/GUI/DesktopIntegrationDialog.cpp -src/slic3r/GUI/DoubleSlider.cpp +src/slic3r/GUI/DoubleSliderForLayers.cpp src/slic3r/GUI/Downloader.cpp src/slic3r/GUI/DownloaderFileGet.cpp src/slic3r/GUI/EditGCodeDialog.cpp diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index 939f8bdd51..a78fdb05ad 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -14,6 +14,11 @@ class DynamicPrintConfig; namespace CustomGCode { +/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. + * So, let use same value as a permissible error for layer height. + */ +constexpr double epsilon() { return 0.0011; } + enum Type { ColorChange, diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 89f094d448..f877148a03 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -252,10 +252,12 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp - GUI/DoubleSlider.cpp - GUI/DoubleSlider.hpp GUI/ImGuiDoubleSlider.cpp GUI/ImGuiDoubleSlider.hpp + GUI/DoubleSliderForLayers.cpp + GUI/DoubleSliderForLayers.hpp + GUI/DoubleSliderForGcode.cpp + GUI/DoubleSliderForGcode.hpp GUI/Notebook.cpp GUI/Notebook.hpp GUI/TopBar.cpp diff --git a/src/slic3r/GUI/DoubleSliderForGcode.cpp b/src/slic3r/GUI/DoubleSliderForGcode.cpp new file mode 100644 index 0000000000..65e8663b1d --- /dev/null +++ b/src/slic3r/GUI/DoubleSliderForGcode.cpp @@ -0,0 +1,33 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#include "DoubleSliderForGcode.hpp" + +namespace Slic3r { + +namespace DoubleSlider { + +static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar +static const float HORIZONTAL_SLIDER_HEIGHT = 90.0f; + +void DSForGcode::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) +{ + if (!m_ctrl.IsShown()) + return; + m_scale = extra_scale * 0.1f * m_em; + + ImVec2 pos = ImVec2{std::max(LEFT_MARGIN, 0.2f * canvas_width), canvas_height - HORIZONTAL_SLIDER_HEIGHT * m_scale}; + ImVec2 size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_HEIGHT * m_scale); + + m_ctrl.Init(pos, size, m_scale); + if (m_ctrl.render()) + process_thumb_move(); +} + +} // DoubleSlider + +} // Slic3r + + diff --git a/src/slic3r/GUI/DoubleSliderForGcode.hpp b/src/slic3r/GUI/DoubleSliderForGcode.hpp new file mode 100644 index 0000000000..c830701637 --- /dev/null +++ b/src/slic3r/GUI/DoubleSliderForGcode.hpp @@ -0,0 +1,42 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_GUI_DoubleSliderForGcode_hpp_ +#define slic3r_GUI_DoubleSliderForGcode_hpp_ + +#include "ImGuiDoubleSlider.hpp" + +namespace Slic3r { + +namespace DoubleSlider { + +class DSForGcode : public Manager +{ +public: + DSForGcode() : Manager() {} + DSForGcode( int lowerPos, + int higherPos, + int minPos, + int maxPos) + { + Init(lowerPos, higherPos, minPos, maxPos, "moves_slider", true); + } + ~DSForGcode() {} + + void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + + void set_render_as_disabled(bool value) { m_render_as_disabled = value; } + bool is_rendering_as_disabled() const { return m_render_as_disabled; } + +private: + + bool m_render_as_disabled{ false }; +}; + +} // DoubleSlider; + +} // Slic3r + + +#endif // slic3r_GUI_DoubleSliderForGcode_hpp_ diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSliderForLayers.cpp similarity index 84% rename from src/slic3r/GUI/DoubleSlider.cpp rename to src/slic3r/GUI/DoubleSliderForLayers.cpp index 0823a58977..ded272e298 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.cpp @@ -2,8 +2,9 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ +#include "DoubleSliderForLayers.hpp" + #include "libslic3r/libslic3r.h" -#include "DoubleSlider.hpp" #include "libslic3r/GCode.hpp" #include "GUI.hpp" #include "GUI_App.hpp" @@ -14,8 +15,6 @@ #include "libslic3r/AppConfig.hpp" #include "GUI_Utils.hpp" #include "MsgDialog.hpp" -#include "Tab.hpp" -#include "GUI_ObjectList.hpp" #include #include @@ -24,7 +23,7 @@ #include #include #include -#include "Field.hpp" + #include "format.hpp" #include "NotificationManager.hpp" @@ -44,9 +43,7 @@ namespace DoubleSlider { constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 -static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar -static const float HORIZONTAL_SLIDER_WIDTH = 90.0f; -static const float VERTICAL_SLIDER_HEIGHT = 105.0f; +static const float VERTICAL_SLIDER_WIDTH = 105.0f; bool equivalent_areas(const double& bottom_area, const double& top_area) { @@ -55,7 +52,7 @@ bool equivalent_areas(const double& bottom_area, const double& top_area) static std::string gcode(Type type) { - const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); + const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); // ! ys_FIXME switch (type) { case ColorChange: return config.color_change_gcode; case PausePrint: return config.pause_print_gcode; @@ -64,7 +61,26 @@ static std::string gcode(Type type) } } -int DSManagerForLayers::get_tick_from_value(double value, bool force_lower_bound/* = false*/) +DSForLayers::DSForLayers( int lowerValue, + int higherValue, + int minValue, + int maxValue, + bool allow_editing) : + m_allow_editing(allow_editing) +{ +#ifdef __WXOSX__ + is_osx = true; +#endif //__WXOSX__ + Init(lowerValue, higherValue, minValue, maxValue, "layers_slider", false); + + m_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); + m_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); + + m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); + m_ticks.set_extruder_colors(&m_extruder_colors); +} + +int DSForLayers::get_tick_from_value(double value, bool force_lower_bound/* = false*/) { std::vector::iterator it; if (m_is_wipe_tower && !force_lower_bound) @@ -78,7 +94,7 @@ int DSManagerForLayers::get_tick_from_value(double value, bool force_lower_bound return int(it - m_values.begin()); } -Info DSManagerForLayers::GetTicksValues() const +Info DSForLayers::GetTicksValues() const { Info custom_gcode_per_print_z; std::vector& values = custom_gcode_per_print_z.gcodes; @@ -91,13 +107,12 @@ Info DSManagerForLayers::GetTicksValues() const values.emplace_back(CustomGCode::Item{ m_values[tick.tick], tick.type, tick.extruder, tick.color, tick.extra }); } - if (m_force_mode_apply) - custom_gcode_per_print_z.mode = m_mode; + custom_gcode_per_print_z.mode = m_mode; return custom_gcode_per_print_z; } -void DSManagerForLayers::SetTicksValues(const Info& custom_gcode_per_print_z) +void DSForLayers::SetTicksValues(const Info& custom_gcode_per_print_z) { if (m_values.empty()) { m_ticks.mode = m_mode; @@ -116,18 +131,18 @@ void DSManagerForLayers::SetTicksValues(const Info& custom_gcode_per_print_z) if (!was_empty && m_ticks.empty()) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one - post_ticks_changed_event(); + process_ticks_changed(); // init extruder sequence in respect to the extruders count if (m_ticks.empty()) m_extruders_sequence.init(m_extruder_colors.size()); - update_callbacks(); + update_draw_scroll_line_cb(); if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; } -void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times, float total_time) +void DSForLayers::SetLayersTimes(const std::vector& layers_times, float total_time) { m_layers_times.clear(); if (layers_times.empty()) @@ -153,7 +168,7 @@ void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times, } } -void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times) +void DSForLayers::SetLayersTimes(const std::vector& layers_times) { m_is_wipe_tower = false; m_layers_times = layers_times; @@ -161,16 +176,16 @@ void DSManagerForLayers::SetLayersTimes(const std::vector& layers_times) m_layers_times[i] += m_layers_times[i - 1]; } -void DSManagerForLayers::SetDrawMode(bool is_sla_print, bool is_sequential_print) +void DSForLayers::SetDrawMode(bool is_sla_print, bool is_sequential_print) { m_draw_mode = is_sla_print ? dmSlaPrint : is_sequential_print ? dmSequentialFffPrint : dmRegular; - update_callbacks(); + update_draw_scroll_line_cb(); } -void DSManagerForLayers::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) +void DSForLayers::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder) { m_mode = !is_one_extruder_printed_model ? MultiExtruder : only_extruder < 0 ? SingleExtruder : @@ -185,12 +200,12 @@ void DSManagerForLayers::SetModeAndOnlyExtruder(const bool is_one_extruder_print m_is_wipe_tower = m_mode != SingleExtruder; } -void DSManagerForLayers::SetExtruderColors( const std::vector& extruder_colors) +void DSForLayers::SetExtruderColors( const std::vector& extruder_colors) { m_extruder_colors = extruder_colors; } -bool DSManagerForLayers::IsNewPrint(const std::string& idxs) +bool DSForLayers::IsNewPrint(const std::string& idxs) { if (idxs == "sla" || idxs == m_print_obj_idxs) return false; @@ -199,17 +214,17 @@ bool DSManagerForLayers::IsNewPrint(const std::string& idxs) return true; } -void DSManagerForLayers::update_callbacks() +void DSForLayers::update_draw_scroll_line_cb() { - if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) - imgui_ctrl.set_draw_scroll_line_cb(nullptr); + if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint || m_draw_mode == dmSlaPrint) + m_ctrl.set_draw_scroll_line_cb(nullptr); else - imgui_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); + m_ctrl.set_draw_scroll_line_cb([this](const ImRect& scroll_line, const ImRect& slideable_region) { draw_colored_band(scroll_line, slideable_region); }); } using namespace ImGui; -void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) +void DSForLayers::draw_ticks(const ImRect& slideable_region) { //if(m_draw_mode != dmRegular) // return; @@ -231,7 +246,7 @@ void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) const ImU32 tick_hovered_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); auto get_tick_pos = [this, slideable_region](int tick) { - return imgui_ctrl.GetPositionFromValue(tick, slideable_region); + return m_ctrl.GetPositionInRect(tick, slideable_region); }; std::set::const_iterator tick_it = m_ticks.ticks.begin(); @@ -250,7 +265,7 @@ void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) ++tick_it; } - auto active_tick_it = m_ticks.ticks.find(TickCode{ imgui_ctrl.GetActiveValue() }); + auto active_tick_it = m_ticks.ticks.find(TickCode{ m_ctrl.GetActivePos() }); tick_it = m_ticks.ticks.begin(); while (tick_it != m_ticks.ticks.end()) @@ -270,10 +285,10 @@ void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) bool activate_this_tick = false; if (tick_it == active_tick_it && m_allow_editing) { // delete tick - if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, imgui_ctrl.IsActiveHigherThumb() ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { + if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, m_ctrl.IsActiveHigherThumb() ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { Type type = tick_it->type; m_ticks.ticks.erase(tick_it); - post_ticks_changed_event(type); + process_ticks_changed(); break; } } @@ -289,7 +304,7 @@ void DSManagerForLayers::draw_ticks(const ImRect& slideable_region) activate_this_tick = render_button(ImGui::EditGCode, ImGui::EditGCodeHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); if (activate_this_tick) { - imgui_ctrl.IsActiveHigherThumb() ? SetHigherValue(tick_it->tick) : SetLowerValue(tick_it->tick); + m_ctrl.IsActiveHigherThumb() ? SetHigherPos(tick_it->tick) : SetLowerPos(tick_it->tick); break; } @@ -318,7 +333,7 @@ static std::array decode_color_to_float_array(const std::string color) return ret; } -void DSManagerForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) +void DSForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) { if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) return; @@ -352,7 +367,7 @@ void DSManagerForLayers::draw_colored_band(const ImRect& groove, const ImRect& s while (tick_it != m_ticks.ticks.end()) { //get position from tick - tick_pos = imgui_ctrl.GetPositionFromValue(tick_it->tick, slideable_region); + tick_pos = m_ctrl.GetPositionInRect(tick_it->tick, slideable_region); ImRect band_rect = ImRect(ImVec2(main_band.Min.x, std::min(tick_pos, main_band.Min.y)), ImVec2(main_band.Max.x, std::min(tick_pos, main_band.Max.y))); @@ -380,7 +395,7 @@ void DSManagerForLayers::draw_colored_band(const ImRect& groove, const ImRect& s } } -void DSManagerForLayers::render_menu() +void DSForLayers::render_menu() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f) * m_scale); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_scale); @@ -391,14 +406,14 @@ void DSManagerForLayers::render_menu() std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); int extruder_num = colors.size(); - if (imgui_ctrl.is_rclick_on_thumb()) { + if (m_ctrl.IsRClickOnThumb()) { ImGui::OpenPopup("slider_menu_popup"); } ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); if (ImGui::BeginPopup("slider_menu_popup")) { - if ((!imgui_ctrl.IsActiveHigherThumb() && GetLowerValueD() == 0.0) || - (imgui_ctrl.IsActiveHigherThumb() && GetHigherValueD() == 0.0)) + if ((!m_ctrl.IsActiveHigherThumb() && GetLowerValue() == 0.0) || + (m_ctrl.IsActiveHigherThumb() && GetHigherValue() == 0.0)) { menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, false); } @@ -442,7 +457,7 @@ void DSManagerForLayers::render_menu() ImGui::PopStyleVar(3); } -bool DSManagerForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) +bool DSForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) { float scale = (float)wxGetApp().em_unit() / 10.0f; @@ -468,7 +483,7 @@ bool DSManagerForLayers::render_button(const wchar_t btn_icon, const wchar_t btn ImGuiContext& g = *GImGui; m_focus = focus; - std::string tooltip = m_allow_editing ? into_u8(get_tooltip(tick)) : ""; + std::string tooltip = m_allow_editing ? get_tooltip(tick) : ""; ImGui::SetCursorPos(ImVec2(0, 0)); const bool ret = m_imgui->image_button(g.HoveredWindow == g.CurrentWindow ? btn_icon_hovered : btn_icon, tooltip, false); @@ -480,25 +495,11 @@ bool DSManagerForLayers::render_button(const wchar_t btn_icon, const wchar_t btn return ret; } -void DSManagerForGcode::imgui_render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) +void DSForLayers::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) { - if (!imgui_ctrl.IsShown()) + if (!m_ctrl.IsShown()) return; - m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); - - ImVec2 pos = ImVec2{std::max(LEFT_MARGIN, 0.2f * canvas_width), canvas_height - HORIZONTAL_SLIDER_WIDTH * m_scale}; - ImVec2 size = ImVec2(canvas_width - 2 * pos.x, HORIZONTAL_SLIDER_WIDTH * m_scale); - - imgui_ctrl.Init(pos, size, m_scale); - if (imgui_ctrl.render()) - process_thumb_move(); -} - -void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) -{ - if (!imgui_ctrl.IsShown()) - return; - m_scale = extra_scale * 0.1f * wxGetApp().em_unit(); + m_scale = extra_scale * 0.1f * m_em; const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; @@ -506,15 +507,15 @@ void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_h ImVec2 pos; ImVec2 size; - pos.x = canvas_width - VERTICAL_SLIDER_HEIGHT * m_scale - tick_icon_side; + pos.x = canvas_width - VERTICAL_SLIDER_WIDTH * m_scale - tick_icon_side; pos.y = 1.f * action_btn_sz; if (m_allow_editing) pos.y += 2.f; - size = ImVec2(VERTICAL_SLIDER_HEIGHT * m_scale, canvas_height - 4.f * action_btn_sz); - imgui_ctrl.ShowLabelOnMouseMove(true); + size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz); + m_ctrl.ShowLabelOnMouseMove(true); - imgui_ctrl.Init(pos, size, m_scale); - if (imgui_ctrl.render()) { + m_ctrl.Init(pos, size, m_scale); + if (m_ctrl.render()) { // request one more frame if value was changes with mouse wheel if (GImGui->IO.MouseWheel != 0.0f) wxGetApp().imgui()->set_requires_extra_frame(); @@ -523,7 +524,7 @@ void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_h // draw action buttons - const float groove_center_x = imgui_ctrl.GetGrooveRect().GetCenter().x; + const float groove_center_x = m_ctrl.GetGrooveRect().GetCenter().x; ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); @@ -532,7 +533,7 @@ void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_h discard_all_thicks(); btn_pos.y += 0.1f * action_btn_sz + size.y; - const bool is_one_layer = imgui_ctrl.IsCombineThumbs(); + const bool is_one_layer = m_ctrl.IsCombineThumbs(); if (render_button(is_one_layer ? ImGui::Lock : ImGui::Unlock, is_one_layer ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) ChangeOneLayerLock(); @@ -540,7 +541,7 @@ void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_h if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) show_cog_icon_context_menu(); - if (m_draw_mode == dmSequentialFffPrint && imgui_ctrl.is_rclick_on_thumb()) { + if (m_draw_mode == dmSequentialFffPrint && m_ctrl.IsRClickOnThumb()) { std::string tooltip = _u8L("The sequential print is on.\n" "It's impossible to apply any custom G-code for objects printing sequentually."); ImGuiPureWrap::tooltip(tooltip, ImGui::GetFontSize() * 20.0f); @@ -549,7 +550,7 @@ void DSManagerForLayers::imgui_render(const int canvas_width, const int canvas_h render_menu(); } -bool DSManagerForLayers::is_wipe_tower_layer(int tick) const +bool DSForLayers::is_wipe_tower_layer(int tick) const { if (!m_is_wipe_tower || tick >= (int)m_values.size()) return false; @@ -603,7 +604,7 @@ static std::string short_and_splitted_time(const std::string& time) return get_s(); } -std::string DSManagerForLayers::get_label(int pos, LabelType label_type/* = ltHeightWithLayer*/) const +std::string DSForLayers::get_label(int pos, LabelType label_type/* = ltHeightWithLayer*/) const { const size_t value = pos; @@ -651,7 +652,7 @@ std::string DSManagerForLayers::get_label(int pos, LabelType label_type/* = ltHe return ""; } -std::string DSManagerForLayers::get_color_for_tool_change_tick(std::set::const_iterator it) const +std::string DSForLayers::get_color_for_tool_change_tick(std::set::const_iterator it) const { const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; @@ -665,7 +666,7 @@ std::string DSManagerForLayers::get_color_for_tool_change_tick(std::set::const_iterator it) const +std::string DSForLayers::get_color_for_color_change_tick(std::set::const_iterator it) const { const int def_extruder = std::max(1, m_only_extruder); auto it_n = it; @@ -687,48 +688,48 @@ std::string DSManagerForLayers::get_color_for_color_change_tick(std::set std::string { // when the tooltip is too long, it starts to flicker, see: https://github.com/prusa3d/PrusaSlicer/issues/7368 // so we limit the number of lines shown std::vector lines; @@ -782,50 +783,50 @@ wxString DSManagerForLayers::get_tooltip(int tick/*=-1*/) tooltip += tick_code_it->type == ColorChange ? (m_mode == SingleExtruder ? - format_wxstr(_L("Color change (\"%1%\")"), gcode(ColorChange)) : - format_wxstr(_L("Color change (\"%1%\") for Extruder %2%"), gcode(ColorChange), tick_code_it->extruder)) : + GUI::format(_L("Color change (\"%1%\")"), gcode(ColorChange)) : + GUI::format(_L("Color change (\"%1%\") for Extruder %2%"), gcode(ColorChange), tick_code_it->extruder)) : tick_code_it->type == PausePrint ? - format_wxstr(_L("Pause print (\"%1%\")"), gcode(PausePrint)) : + GUI::format(_L("Pause print (\"%1%\")"), gcode(PausePrint)) : tick_code_it->type == Template ? - format_wxstr(_L("Custom template (\"%1%\")"), gcode(Template)) : + GUI::format(_L("Custom template (\"%1%\")"), gcode(Template)) : tick_code_it->type == ToolChange ? - format_wxstr(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : - from_u8(format_gcode(tick_code_it->extra));// tick_code_it->type == Custom + GUI::format(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : + format_gcode(tick_code_it->extra);// tick_code_it->type == Custom // If tick is marked as a conflict (exclamation icon), // we should to explain why ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); if (conflict != ctNone) - tooltip += "\n\n" + _L("Note") + "! "; + tooltip += "\n\n" + _u8L("Note") + "! "; if (conflict == ctModeConflict) - tooltip += _L("G-code associated to this tick mark is in a conflict with print mode.\n" + tooltip += _u8L("G-code associated to this tick mark is in a conflict with print mode.\n" "Editing it will cause changes of Slider data."); else if (conflict == ctMeaninglessColorChange) - tooltip += _L("There is a color change for extruder that won't be used till the end of print job.\n" + tooltip += _u8L("There is a color change for extruder that won't be used till the end of print job.\n" "This code won't be processed during G-code generation."); else if (conflict == ctMeaninglessToolChange) - tooltip += _L("There is an extruder change set to the same extruder.\n" + tooltip += _u8L("There is an extruder change set to the same extruder.\n" "This code won't be processed during G-code generation."); else if (conflict == ctRedundant) - tooltip += _L("There is a color change for extruder that has not been used before.\n" + tooltip += _u8L("There is a color change for extruder that has not been used before.\n" "Check your settings to avoid redundant color changes."); // Show list of actions with existing tick if (m_focus == fiActionIcon) - tooltip += "\n\n" + _L("Delete tick mark - Left click or press \"-\" key") + "\n" + ( + tooltip += "\n\n" + _u8L("Delete tick mark - Left click or press \"-\" key") + "\n" + ( is_osx ? - _L("Edit tick mark - Ctrl + Left click") : - _L("Edit tick mark - Right click") ); + _u8L("Edit tick mark - Ctrl + Left click") : + _u8L("Edit tick mark - Right click") ); } - return tooltip; + return tooltip; } -void DSManagerForLayers::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +void DSForLayers::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - std::array active_extruders = get_active_extruders_for_tick(imgui_ctrl.GetActiveValue()); + std::array active_extruders = get_active_extruders_for_tick(m_ctrl.GetActivePos()); std::vector icons = get_extruder_color_icons(true); @@ -848,15 +849,15 @@ void DSManagerForLayers::append_change_extruder_menu_item(wxMenu* menu, bool swi append_submenu(menu, change_extruder_menu, wxID_ANY, change_extruder_menu_name, _L("Use another extruder"), active_extruders[1] > 0 ? "edit_uni" : "change_extruder", - [this]() {return m_mode == MultiAsSingle && !GUI::wxGetApp().obj_list()->has_paint_on_segmentation(); }, GUI::wxGetApp().plater()); + [this]() {return m_mode == MultiAsSingle /*&& !GUI::wxGetApp().obj_list()->has_paint_on_segmentation()*/; }, GUI::wxGetApp().plater()); // !ysFIXME has_paint_on_segmentation } } -void DSManagerForLayers::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) +void DSForLayers::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) { const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); if (extruders_cnt > 1) { - int tick = imgui_ctrl.GetActiveValue(); + int tick = m_ctrl.GetActivePos(); std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); wxMenu* add_color_change_menu = new wxMenu(); @@ -880,7 +881,7 @@ void DSManagerForLayers::append_add_color_change_menu_item(wxMenu* menu, bool sw } } -void DSManagerForLayers::UseDefaultColors(bool def_colors_on) +void DSForLayers::UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } @@ -889,7 +890,7 @@ void DSManagerForLayers::UseDefaultColors(bool def_colors_on) // Means one current extruder for not existing tick OR // 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick) // Use those values to disable selection of active extruders -std::array DSManagerForLayers::get_active_extruders_for_tick(int tick) const +std::array DSForLayers::get_active_extruders_for_tick(int tick) const { int default_initial_extruder = m_mode == MultiAsSingle ? std::max(1, m_only_extruder) : 1; std::array extruders = { default_initial_extruder, -1 }; @@ -912,73 +913,14 @@ std::array DSManagerForLayers::get_active_extruders_for_tick(int tick) c return extruders; } -// Get used extruders for tick. -// Means all extruders(tools) which will be used during printing from current tick to the end -std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode/* = Undef*/) const -{ - Mode e_mode = !force_mode ? mode : force_mode; - - if (e_mode == MultiExtruder) { - // #ys_FIXME: get tool ordering from _correct_ place - const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - - if (tool_ordering.empty()) - return {}; - - std::set used_extruders; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools &lhs, double rhs){ return lhs.print_z < rhs; }); - for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) - used_extruders.emplace(extruder+1); - } - - return used_extruders; - } - - const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder, 1) : 1; - if (ticks.empty() || e_mode == SingleExtruder) - return {default_initial_extruder}; - - std::set used_extruders; - - auto it_start = ticks.lower_bound(TickCode{tick}); - auto it = it_start; - if (it == ticks.begin() && it->type == ToolChange && - tick != it->tick ) // In case of switch of ToolChange to ColorChange, when tick exists, - // we shouldn't change color for extruder, which will be deleted - { - used_extruders.emplace(it->extruder); - if (tick < it->tick) - used_extruders.emplace(default_initial_extruder); - } - - while (it != ticks.begin()) { - --it; - if (it->type == ToolChange && tick != it->tick) { - used_extruders.emplace(it->extruder); - break; - } - } - - if (it == ticks.begin() && used_extruders.empty()) - used_extruders.emplace(default_initial_extruder); - - for (it = it_start; it != ticks.end(); ++it) - if (it->type == ToolChange && tick != it->tick) - used_extruders.emplace(it->extruder); - - return used_extruders; -} - -void DSManagerForLayers::show_cog_icon_context_menu() +void DSForLayers::show_cog_icon_context_menu() { wxMenu menu; append_menu_item(&menu, wxID_ANY, _L("Jump to height") + " (Shift+G)", "", [this](wxCommandEvent&) { jump_to_value(); }, "", & menu); + if (m_allow_editing) append_menu_check_item(&menu, wxID_ANY, _L("Use default colors"), _L("Use default colors on color change"), [this](wxCommandEvent&) { UseDefaultColors(!m_ticks.used_default_colors()); }, &menu, @@ -1025,7 +967,7 @@ bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t return detected; } -void DSManagerForLayers::auto_color_change() +void DSForLayers::auto_color_change() { if (!m_ticks.empty()) { wxString msg_text = _L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" + @@ -1072,9 +1014,9 @@ void DSManagerForLayers::auto_color_change() if (m_ticks.empty()) GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange); - update_callbacks(); + update_draw_scroll_line_cb(); - post_ticks_changed_event(); + process_ticks_changed(); } static std::string get_new_color(const std::string& color) @@ -1149,7 +1091,7 @@ static std::string get_custom_code(const std::string& code_in, double height) return ""; value = into_u8(dlg.GetValue()); - valid = GUI::Tab::validate_custom_gcode("Custom G-code", value); + valid = true;// GUI::Tab::validate_custom_gcode("Custom G-code", value); // !ysFIXME validate_custom_gcode } while (!valid); return value; } @@ -1170,11 +1112,12 @@ static std::string get_pause_print_msg(const std::string& msg_in, double height) return into_u8(dlg.GetValue()); } +// draw with imgui static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode) { wxString msg_text = _L("Enter the height you want to jump to") + ":"; wxString msg_header = _L("Jump to height"); - wxString msg_in = GUI::double_to_string(active_value); + wxString msg_in = "";// GUI::double_to_string(active_value); // get custom gcode wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle); @@ -1187,9 +1130,9 @@ static double get_value_to_jump(double active_value, double min_z, double max_z, return dlg.GetValue().ToDouble(&value) ? value : -1.0; } -void DSManagerForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) +void DSForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { - const int tick = imgui_ctrl.GetActiveValue(); + const int tick = m_ctrl.GetActivePos(); if ( !check_ticks_changed_event(type) ) return; @@ -1216,14 +1159,17 @@ void DSManagerForLayers::add_code_as_tick(Type type, int selected_extruder/* = - return; if (was_ticks != m_ticks.empty()) - update_callbacks(); + update_draw_scroll_line_cb(); - post_ticks_changed_event(type); + process_ticks_changed(); } -void DSManagerForLayers::add_current_tick(bool call_from_keyboard /*= false*/) +void DSForLayers::add_current_tick() { - const int tick = imgui_ctrl.GetActiveValue(); + if (!m_allow_editing) + return; + + const int tick = m_ctrl.GetActivePos(); auto it = m_ticks.ticks.find(TickCode{ tick }); if (it != m_ticks.ticks.end() || // this tick is already exist @@ -1232,8 +1178,9 @@ void DSManagerForLayers::add_current_tick(bool call_from_keyboard /*= false*/) if (m_mode == SingleExtruder) add_code_as_tick(ColorChange); - /* else + render_menu(); + /* { wxMenu menu; @@ -1263,43 +1210,42 @@ void DSManagerForLayers::add_current_tick(bool call_from_keyboard /*= false*/) */ } -void DSManagerForLayers::delete_current_tick() +void DSForLayers::delete_current_tick() { - auto it = m_ticks.ticks.find(TickCode{ imgui_ctrl.GetActiveValue()}); + auto it = m_ticks.ticks.find(TickCode{ m_ctrl.GetActivePos()}); if (it == m_ticks.ticks.end() || !check_ticks_changed_event(it->type)) return; Type type = it->type; m_ticks.ticks.erase(it); - post_ticks_changed_event(type); + process_ticks_changed(); } -void DSManagerForLayers::edit_tick(int tick/* = -1*/) +void DSForLayers::edit_tick(int tick/* = -1*/) { if (tick < 0) - tick = imgui_ctrl.GetActiveValue(); + tick = m_ctrl.GetActivePos(); const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); if (it == m_ticks.ticks.end() || !check_ticks_changed_event(it->type)) return; - Type type = it->type; if (m_ticks.edit_tick(it, m_values[it->tick])) - post_ticks_changed_event(type); + process_ticks_changed(); } // discard all custom changes on DoubleSlider -void DSManagerForLayers::discard_all_thicks() +void DSForLayers::discard_all_thicks() { m_ticks.ticks.clear(); - imgui_ctrl.ResetValues(); - update_callbacks(); - post_ticks_changed_event(); + m_ctrl.ResetPositions(); + update_draw_scroll_line_cb(); + process_ticks_changed(); } -void DSManagerForLayers::edit_extruder_sequence() +void DSForLayers::edit_extruder_sequence() { if (!check_ticks_changed_event(ToolChange)) return; @@ -1323,7 +1269,7 @@ void DSManagerForLayers::edit_extruder_sequence() std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() std::uniform_int_distribution<> distrib(0, extr_cnt-1); - while (tick <= imgui_ctrl.GetMaxValue()) + while (tick <= m_ctrl.GetMaxPos()) { bool color_repetition = false; if (m_extruders_sequence.random_sequence) { @@ -1357,31 +1303,25 @@ void DSManagerForLayers::edit_extruder_sequence() tick += m_extruders_sequence.interval_by_layers; } - post_ticks_changed_event(ToolChange); + process_ticks_changed(); } -void DSManagerForLayers::jump_to_value() +void DSForLayers::jump_to_value() { - double value = get_value_to_jump(m_values[imgui_ctrl.GetActiveValue()], - m_values[imgui_ctrl.GetMinValue()], m_values[imgui_ctrl.GetMaxValue()], m_draw_mode); + double value = get_value_to_jump(m_values[m_ctrl.GetActivePos()], + m_values[m_ctrl.GetMinPos()], m_values[m_ctrl.GetMaxPos()], m_draw_mode); if (value < 0.0) return; int tick_value = get_tick_from_value(value); - if (imgui_ctrl.IsActiveHigherThumb()) - SetHigherValue(tick_value); + if (m_ctrl.IsActiveHigherThumb()) + SetHigherPos(tick_value); else - SetLowerValue(tick_value); + SetLowerPos(tick_value); } -void DSManagerForLayers::post_ticks_changed_event(Type type /*= Custom*/) -{ -// m_force_mode_apply = type != ToolChange; // It looks like this condition is no needed now. Leave it for the testing - process_ticks_changed(); -} - -bool DSManagerForLayers::check_ticks_changed_event(Type type) +bool DSForLayers::check_ticks_changed_event(Type type) { if ( m_ticks.mode == m_mode || (type != ColorChange && type != ToolChange) || @@ -1406,7 +1346,7 @@ bool DSManagerForLayers::check_ticks_changed_event(Type type) GUI::MessageDialog msg(nullptr, message, _L("Notice"), wxYES_NO); if (msg.ShowModal() == wxID_YES) { m_ticks.erase_all_ticks_with_code(ColorChange); - post_ticks_changed_event(ColorChange); + process_ticks_changed(); } return false; } @@ -1427,11 +1367,11 @@ bool DSManagerForLayers::check_ticks_changed_event(Type type) const int answer = msg.ShowModal(); if (answer == wxID_YES) { m_ticks.erase_all_ticks_with_code(ToolChange); - post_ticks_changed_event(ToolChange); + process_ticks_changed(); } else if (m_mode == SingleExtruder && answer == wxID_NO) { m_ticks.switch_code(ToolChange, ColorChange); - post_ticks_changed_event(ColorChange); + process_ticks_changed(); } return false; } @@ -1439,23 +1379,67 @@ bool DSManagerForLayers::check_ticks_changed_event(Type type) return true; } -DSManagerForLayers::DSManagerForLayers( int lowerValue, - int higherValue, - int minValue, - int maxValue, - bool allow_editing/* = true*/) : - m_allow_editing(allow_editing) + + + +// Get used extruders for tick. +// Means all extruders(tools) which will be used during printing from current tick to the end +std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode/* = Undef*/) const { -#ifdef __WXOSX__ - is_osx = true; -#endif //__WXOSX__ - Init(lowerValue, higherValue, minValue, maxValue, "layers_slider", false); + Mode e_mode = !force_mode ? mode : force_mode; - imgui_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); - imgui_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); + if (e_mode == MultiExtruder) { + // #ys_FIXME: get tool ordering from _correct_ place + const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); - m_ticks.set_extruder_colors(&m_extruder_colors); + if (tool_ordering.empty()) + return {}; + + std::set used_extruders; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools& lhs, double rhs) { return lhs.print_z < rhs; }); + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { + const std::vector& extruders = it_layer_tools->extruders; + for (const auto& extruder : extruders) + used_extruders.emplace(extruder + 1); + } + + return used_extruders; + } + + const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder, 1) : 1; + if (ticks.empty() || e_mode == SingleExtruder) + return { default_initial_extruder }; + + std::set used_extruders; + + auto it_start = ticks.lower_bound(TickCode{ tick }); + auto it = it_start; + if (it == ticks.begin() && it->type == ToolChange && + tick != it->tick) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { + used_extruders.emplace(it->extruder); + if (tick < it->tick) + used_extruders.emplace(default_initial_extruder); + } + + while (it != ticks.begin()) { + --it; + if (it->type == ToolChange && tick != it->tick) { + used_extruders.emplace(it->extruder); + break; + } + } + + if (it == ticks.begin() && used_extruders.empty()) + used_extruders.emplace(default_initial_extruder); + + for (it = it_start; it != ticks.end(); ++it) + if (it->type == ToolChange && tick != it->tick) + used_extruders.emplace(it->extruder); + + return used_extruders; } std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder) diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSliderForLayers.hpp similarity index 51% rename from src/slic3r/GUI/DoubleSlider.hpp rename to src/slic3r/GUI/DoubleSliderForLayers.hpp index 5a4c08d701..e486371f6a 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.hpp @@ -2,8 +2,8 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ -#ifndef slic3r_GUI_DoubleSlider_hpp_ -#define slic3r_GUI_DoubleSlider_hpp_ +#ifndef slic3r_GUI_DoubleSliderForLayers_hpp_ +#define slic3r_GUI_DoubleSliderForLayers_hpp_ #include "libslic3r/CustomGCode.hpp" #include "ImGuiDoubleSlider.hpp" @@ -23,11 +23,6 @@ namespace DoubleSlider { using namespace GUI; -/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. - * So, let use same value as a permissible error for layer height. - */ -constexpr double epsilon() { return 0.0011; } - // return true when areas are mostly equivalent bool equivalent_areas(const double& bottom_area, const double& top_area); @@ -60,20 +55,6 @@ enum ConflictType ctRedundant }; -enum MouseAction -{ - maNone, - maAddMenu, // show "Add" context menu for NOTexist active tick - maEditMenu, // show "Edit" context menu for exist active tick - maCogIconMenu, // show context for "cog" icon - maForceColorEdit, // force color editing from colored band - maAddTick, // force tick adding - maDeleteTick, // force tick deleting - maCogIconClick, // LeftMouseClick on "cog" icon - maOneLayerIconClick, // LeftMouseClick on "one_layer" icon - maRevertIconClick, // LeftMouseClick on "revert" icon -}; - enum DrawMode { dmRegular, @@ -192,148 +173,17 @@ struct ExtrudersSequence } }; -template -class DSManager_t + +class DSForLayers : public Manager { public: - - void Init( int lowerValue, + DSForLayers() : Manager() {} + DSForLayers(int lowerValue, int higherValue, int minValue, int maxValue, - const std::string& name, - bool is_horizontal) - { - imgui_ctrl = ImGuiControl( lowerValue, higherValue, - minValue, maxValue, - is_horizontal ? 0 : ImGuiSliderFlags_Vertical, - name, !is_horizontal); - - imgui_ctrl.set_get_label_cb([this](int pos) {return get_label(pos); }); - }; - - DSManager_t() {} - DSManager_t(int lowerValue, - int higherValue, - int minValue, - int maxValue, - const std::string& name, - bool is_horizontal) - { - Init (lowerValue, higherValue, minValue, maxValue, name, is_horizontal); - } - ~DSManager_t() {} - - int GetMinValue() const { return imgui_ctrl.GetMinValue(); } - int GetMaxValue() const { return imgui_ctrl.GetMaxValue(); } - int GetLowerValue() const { return imgui_ctrl.GetLowerValue(); } - int GetHigherValue()const { return imgui_ctrl.GetHigherValue(); } - - ValType GetMinValueD() { return m_values.empty() ? static_cast(0) : m_values[GetMinValue()]; } - ValType GetMaxValueD() { return m_values.empty() ? static_cast(0) : m_values[GetMaxValue()]; } - ValType GetLowerValueD() { return m_values.empty() ? static_cast(0) : m_values[GetLowerValue()];} - ValType GetHigherValueD() { return m_values.empty() ? static_cast(0) : m_values[GetHigherValue()]; } - - // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetLowerValue(const int lower_val) { - imgui_ctrl.SetLowerValue(lower_val); - process_thumb_move(); - } - void SetHigherValue(const int higher_val) { - imgui_ctrl.SetHigherValue(higher_val); - process_thumb_move(); - } - void SetSelectionSpan(const int lower_val, const int higher_val) { - imgui_ctrl.SetSelectionSpan(lower_val, higher_val); - process_thumb_move(); - } - void SetMaxValue(const int max_value) { - imgui_ctrl.SetMaxValue(max_value); - process_thumb_move(); - } - - void SetSliderValues(const std::vector& values) { m_values = values; } - // values used to show thumb labels - void SetSliderAlternateValues(const std::vector& values) { m_alternate_values = values; } - - bool is_lower_at_min() const { return imgui_ctrl.is_lower_at_min(); } - bool is_higher_at_max() const { return imgui_ctrl.is_higher_at_max(); } - - void Show(bool show = true) { imgui_ctrl.Show(show); } - void Hide() { Show(false); } - - virtual void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) = 0; - - void set_callback_on_thumb_move(std::function cb) { m_cb_thumb_move = cb; }; - - void move_current_thumb(const int delta) - { - imgui_ctrl.MoveActiveThumb(delta); - process_thumb_move(); - } - -protected: - - std::vector m_values; - std::vector m_alternate_values; - - GUI::ImGuiControl imgui_ctrl; - float m_scale{ 1.f }; - - std::string get_label(int pos) const { - if (m_values.empty()) - return GUI::format("%1%", pos); - if (pos >= m_values.size()) - return "ErrVal"; - return GUI::format("%1%", static_cast(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos])); - } - - void process_thumb_move() { - if (m_cb_thumb_move) - m_cb_thumb_move(); - } - -private: - - std::function m_cb_thumb_move{ nullptr }; -}; - - -class DSManagerForGcode : public DSManager_t -{ -public: - DSManagerForGcode() : DSManager_t() {} - DSManagerForGcode( int lowerValue, - int higherValue, - int minValue, - int maxValue) - { - Init(lowerValue, higherValue, minValue, maxValue, "moves_slider", true); - } - ~DSManagerForGcode() {} - - void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; - - void set_render_as_disabled(bool value) { m_render_as_disabled = value; } - bool is_rendering_as_disabled() const { return m_render_as_disabled; } - -private: - - bool m_render_as_disabled{ false }; -}; - - - -class DSManagerForLayers : public DSManager_t -{ -public: - DSManagerForLayers() : DSManager_t() {} - DSManagerForLayers( int lowerValue, - int higherValue, - int minValue, - int maxValue, - bool allow_editing = true); - ~DSManagerForLayers() {} + bool allow_editing); + ~DSForLayers() {} void ChangeOneLayerLock(); @@ -349,35 +199,38 @@ public: void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); void SetExtruderColors(const std::vector& extruder_colors); - void UseDefaultColors(bool def_colors_on); - - void add_code_as_tick(Type type, int selected_extruder = -1); - // add default action for tick, when press "+" - void add_current_tick(bool call_from_keyboard = false); - // delete current tick, when press "-" - void delete_current_tick(); - void edit_tick(int tick = -1); - void discard_all_thicks(); - void edit_extruder_sequence(); - void enable_action_icon(bool enable) { m_enable_action_icon = enable; } - void show_cog_icon_context_menu(); - void auto_color_change(); - void jump_to_value(); - - void set_callback_on_ticks_changed(std::function cb) { m_cb_ticks_changed = cb; }; - - void imgui_render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; - + void UseDefaultColors(bool def_colors_on); bool IsNewPrint(const std::string& print_obj_idxs); + void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + + void set_callback_on_ticks_changed(std::function cb) { m_cb_ticks_changed = cb; }; + + // manipulation with slider from keyboard + + // add default action for tick, when press "+" + void add_current_tick(); + // delete current tick, when press "-" + void delete_current_tick(); + // process adding of auto color change + void auto_color_change(); + // jump to selected layer + void jump_to_value(); + private: + void add_code_as_tick(Type type, int selected_extruder = -1); + void edit_tick(int tick = -1); + void discard_all_thicks(); + void edit_extruder_sequence(); + void show_cog_icon_context_menu(); + bool is_wipe_tower_layer(int tick) const; - std::string get_label(int tick, LabelType label_type = ltHeightWithLayer) const; + std::string get_label(int tick, LabelType label_type = ltHeightWithLayer) const; int get_tick_from_value(double value, bool force_lower_bound = false); - wxString get_tooltip(int tick = -1); + std::string get_tooltip(int tick = -1); std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; std::string get_color_for_color_change_tick(std::set::const_iterator it) const; @@ -393,48 +246,44 @@ private: // Use those values to disable selection of active extruders std::array get_active_extruders_for_tick(int tick) const; - void post_ticks_changed_event(Type type = Custom); bool check_ticks_changed_event(Type type); - void append_change_extruder_menu_item(wxMenu*, bool switch_current_code = false); - void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); + void append_change_extruder_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME ! + void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME ! - bool is_osx{ false }; - bool m_allow_editing{ true }; + bool is_osx { false }; + bool m_allow_editing { true }; + bool m_is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower + bool m_show_estimated_times { false }; - bool m_force_mode_apply = true; - bool m_enable_action_icon = true; - bool m_is_wipe_tower = false; //This flag indicates that there is multiple extruder print with wipe tower + DrawMode m_draw_mode { dmRegular }; + Mode m_mode { SingleExtruder }; + FocusedItem m_focus { fiNone }; - DrawMode m_draw_mode { dmRegular }; - - Mode m_mode = SingleExtruder; - int m_only_extruder = -1; - - MouseAction m_mouse = maNone; - FocusedItem m_focus = fiNone; - - bool m_show_estimated_times{ false }; - - TickCodeInfo m_ticks; - std::vector m_layers_times; - std::vector m_layers_values; - std::vector m_extruder_colors; - - ExtrudersSequence m_extruders_sequence; - - std::function m_cb_ticks_changed{ nullptr }; + int m_only_extruder { -1 }; std::string m_print_obj_idxs; - // ImGuiDS + std::vector m_layers_times; + std::vector m_layers_values; + std::vector m_extruder_colors; + + TickCodeInfo m_ticks; + + ExtrudersSequence m_extruders_sequence; + + std::function m_cb_ticks_changed{ nullptr }; + bool m_can_change_color{ true }; + // functions for extend rendering of m_ctrl + void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); void render_menu(); bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1); - void update_callbacks(); + + void update_draw_scroll_line_cb(); }; } // DoubleSlider; @@ -443,4 +292,4 @@ private: -#endif // slic3r_GUI_DoubleSlider_hpp_ +#endif // slic3r_GUI_DoubleSliderForLayers_hpp_ diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp index 37046834af..ab523c78c0 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.hpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -6,7 +6,7 @@ #define slic3r_GUI_ExtruderSequenceDialog_hpp_ #include "GUI_Utils.hpp" -#include "DoubleSlider.hpp" +#include "DoubleSliderForLayers.hpp" #include "wxExtensions.hpp" class wxTextCtrl; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 1ada6188c7..d6188f3d92 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -14,6 +14,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/CustomGCode.hpp" #include "slic3r/GUI/format.hpp" @@ -24,7 +25,6 @@ #include "I18N.hpp" #include "GUI_Utils.hpp" #include "GUI.hpp" -#include "DoubleSlider.hpp" #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" @@ -2120,12 +2120,12 @@ void GCodeViewer::render_legend(float& legend_height) if (extruder_id + 1 != static_cast(item.extruder)) continue; - if (item.type != ColorChange) + if (item.type != CustomGCode::ColorChange) continue; const std::vector zs = m_viewer.get_layers_zs(); auto lower_b = std::lower_bound(zs.begin(), zs.end(), - static_cast(item.print_z - Slic3r::DoubleSlider::epsilon())); + static_cast(item.print_z - Slic3r::CustomGCode::epsilon())); if (lower_b == zs.end()) continue; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index e3a9aadfef..07d2717c6a 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -19,7 +19,8 @@ #include "OpenGLManager.hpp" #include "GLCanvas3D.hpp" #include "libslic3r/PresetBundle.hpp" -#include "DoubleSlider.hpp" +#include "DoubleSliderForGcode.hpp" +#include "DoubleSliderForLayers.hpp" #include "Plater.hpp" #include "MainFrame.hpp" #include "format.hpp" @@ -197,8 +198,8 @@ Preview::Preview( void Preview::set_layers_slider_values_range(int bottom, int top) { - m_layers_slider->SetHigherValue(std::min(top, m_layers_slider->GetMaxValue())); - m_layers_slider->SetLowerValue(std::max(bottom, m_layers_slider->GetMinValue())); + m_layers_slider->SetHigherPos(std::min(top, m_layers_slider->GetMaxPos())); + m_layers_slider->SetLowerPos(std::max(bottom, m_layers_slider->GetMinPos())); } bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) @@ -314,6 +315,8 @@ void Preview::reload_print() void Preview::msw_rescale() { + m_layers_slider->SetEmUnit(wxGetApp().em_unit()); + m_moves_slider->SetEmUnit(wxGetApp().em_unit()); // rescale warning legend on the canvas get_canvas3d()->msw_rescale(); @@ -328,9 +331,9 @@ void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) const int canvas_height = cnv_size.get_height(); if (m_layers_slider) - m_layers_slider->imgui_render(canvas_width, canvas_height, extra_scale); + m_layers_slider->Render(canvas_width, canvas_height, extra_scale); if (m_moves_slider) - m_moves_slider->imgui_render(canvas_width, canvas_height, extra_scale); + m_moves_slider->Render(canvas_width, canvas_height, extra_scale); } void Preview::bind_event_handlers() @@ -358,13 +361,12 @@ void Preview::create_sliders() { // Layers Slider - m_layers_slider = new DoubleSlider::DSManagerForLayers(0, 0, 0, 100, wxGetApp().is_editor()); + m_layers_slider = new DoubleSlider::DSForLayers(0, 0, 0, 100, wxGetApp().is_editor()); + m_layers_slider->SetEmUnit(wxGetApp().em_unit()); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); - m_layers_slider->enable_action_icon(wxGetApp().is_editor()); - m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } ); m_layers_slider->set_callback_on_ticks_changed( [this]() -> void { @@ -378,7 +380,8 @@ void Preview::create_sliders() // Move Gcode Slider - m_moves_slider = new DoubleSlider::DSManagerForGcode(0, 0, 0, 100); + m_moves_slider = new DoubleSlider::DSForGcode(0, 0, 0, 100); + m_moves_slider->SetEmUnit(wxGetApp().em_unit()); m_moves_slider->set_callback_on_thumb_move([this]() ->void { Preview::on_moves_slider_scroll_changed(); }); @@ -423,7 +426,7 @@ void Preview::check_layers_slider_values(std::vector& ticks_f ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(), [layers_z](CustomGCode::Item val) { - auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon()); + auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - CustomGCode::epsilon()); return it == layers_z.end(); }), ticks_from_model.end()); @@ -434,17 +437,17 @@ void Preview::check_layers_slider_values(std::vector& ticks_f void Preview::update_layers_slider(const std::vector& layers_z, bool keep_z_range) { // Save the initial slider span. - double z_low = m_layers_slider->GetLowerValueD(); - double z_high = m_layers_slider->GetHigherValueD(); - bool was_empty = m_layers_slider->GetMaxValue() == 0; + double z_low = m_layers_slider->GetLowerValue(); + double z_high = m_layers_slider->GetHigherValue(); + bool was_empty = m_layers_slider->GetMaxPos() == 0; bool force_sliders_full_range = was_empty; if (!keep_z_range) { - bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValueD()) > DoubleSlider::epsilon()/*1e-6*/; + bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_layers_slider->GetMaxValue()) > CustomGCode::epsilon()/*1e-6*/; force_sliders_full_range |= span_changed; } - bool snap_to_min = force_sliders_full_range || m_layers_slider->is_lower_at_min(); - bool snap_to_max = force_sliders_full_range || m_layers_slider->is_higher_at_max(); + bool snap_to_min = force_sliders_full_range || m_layers_slider->IsLowerAtMin(); + bool snap_to_max = force_sliders_full_range || m_layers_slider->IsHigherAtMax(); // Detect and set manipulation mode for double slider update_layers_slider_mode(); @@ -462,19 +465,19 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee //first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result)); m_layers_slider->SetSliderValues(layers_z); - assert(m_layers_slider->GetMinValue() == 0); - m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); + assert(m_layers_slider->GetMinPos() == 0); + m_layers_slider->SetMaxPos(layers_z.empty() ? 0 : layers_z.size() - 1); int idx_low = 0; - int idx_high = m_layers_slider->GetMaxValue(); + int idx_high = m_layers_slider->GetMaxPos(); if (!layers_z.empty()) { if (!snap_to_min) { - int idx_new = find_close_layer_idx(layers_z, z_low, DoubleSlider::epsilon()/*1e-6*/); + int idx_new = find_close_layer_idx(layers_z, z_low, CustomGCode::epsilon()/*1e-6*/); if (idx_new != -1) idx_low = idx_new; } if (!snap_to_max) { - int idx_new = find_close_layer_idx(layers_z, z_high, DoubleSlider::epsilon()/*1e-6*/); + int idx_new = find_close_layer_idx(layers_z, z_high, CustomGCode::epsilon()/*1e-6*/); if (idx_new != -1) idx_high = idx_new; } @@ -625,17 +628,19 @@ void Preview::update_layers_slider_mode() void Preview::reset_layers_slider() { - m_layers_slider->SetHigherValue(0); - m_layers_slider->SetLowerValue(0); + m_layers_slider->SetHigherPos(0); + m_layers_slider->SetLowerPos(0); } void Preview::update_sliders_from_canvas(wxKeyEvent& event) { const auto key = event.GetKeyCode(); - if (key == WXK_NUMPAD_ADD || key == '+') - m_layers_slider->add_current_tick(true); - else if (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK || key == '-') + const bool can_edit = wxGetApp().is_editor(); + + if (can_edit && (key == WXK_NUMPAD_ADD || key == '+')) + m_layers_slider->add_current_tick(); + else if (can_edit && (key == WXK_NUMPAD_SUBTRACT || key == WXK_DELETE || key == WXK_BACK || key == '-')) m_layers_slider->delete_current_tick(); else if (key == 'G' || key == 'g') m_layers_slider->jump_to_value(); @@ -662,12 +667,12 @@ void Preview::update_sliders_from_canvas(wxKeyEvent& event) } else if (key == 'S' || key == 'W') { - const int new_pos = key == 'W' ? m_layers_slider->GetHigherValue() + 1 : m_layers_slider->GetHigherValue() - 1; - m_layers_slider->SetHigherValue(new_pos); + const int new_pos = key == 'W' ? m_layers_slider->GetHigherPos() + 1 : m_layers_slider->GetHigherPos() - 1; + m_layers_slider->SetHigherPos(new_pos); } else if (key == 'A' || key == 'D') { - const int new_pos = key == 'D' ? m_moves_slider->GetHigherValue() + 1 : m_moves_slider->GetHigherValue() - 1; - m_moves_slider->SetHigherValue(new_pos); + const int new_pos = key == 'D' ? m_moves_slider->GetHigherPos() + 1 : m_moves_slider->GetHigherPos() - 1; + m_moves_slider->SetHigherPos(new_pos); } else if (key == 'X') m_layers_slider->ChangeOneLayerLock(); @@ -726,8 +731,10 @@ void Preview::update_moves_slider(std::optional visible_range_min, std::opt m_moves_slider->SetSliderValues(values); m_moves_slider->SetSliderAlternateValues(alternate_values); - m_moves_slider->SetMaxValue(static_cast(values.size()) - 1); + m_moves_slider->SetMaxPos(static_cast(values.size()) - 1); m_moves_slider->SetSelectionSpan(span_min_id, span_max_id); + + m_moves_slider->ShowLowerThumb(get_app_config()->get("seq_top_layer_only") == "0"); } void Preview::enable_moves_slider(bool enable) @@ -888,13 +895,13 @@ void Preview::on_layers_slider_scroll_changed() if (IsShown()) { PrinterTechnology tech = m_process->current_printer_technology(); if (tech == ptFFF) { - m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValueD(), m_layers_slider->GetHigherValueD() }); - m_canvas->set_toolpaths_z_range({ static_cast(m_layers_slider->GetLowerValue()), static_cast(m_layers_slider->GetHigherValue()) }); + m_canvas->set_volumes_z_range({ m_layers_slider->GetLowerValue(), m_layers_slider->GetHigherValue() }); + m_canvas->set_toolpaths_z_range({ static_cast(m_layers_slider->GetLowerPos()), static_cast(m_layers_slider->GetHigherPos()) }); m_canvas->set_as_dirty(); } else if (tech == ptSLA) { - m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValueD())); - m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValueD())); + m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_layers_slider->GetLowerValue())); + m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_layers_slider->GetHigherValue())); m_canvas->render(); } } @@ -902,7 +909,7 @@ void Preview::on_layers_slider_scroll_changed() void Preview::on_moves_slider_scroll_changed() { - m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValueD() - 1.0), static_cast(m_moves_slider->GetHigherValueD() - 1.0)); + m_canvas->update_gcode_sequential_view_current(static_cast(m_moves_slider->GetLowerValue() - 1), static_cast(m_moves_slider->GetHigherValue() - 1)); m_canvas->set_as_dirty(); m_canvas->request_extra_frame(); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 452185fbe1..5118532965 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -28,8 +28,8 @@ class BackgroundSlicingProcess; class Model; namespace DoubleSlider { - class DSManagerForGcode; - class DSManagerForLayers; + class DSForGcode; + class DSForLayers; }; namespace GUI { @@ -96,8 +96,8 @@ class Preview : public wxPanel bool m_loaded { false }; - DoubleSlider::DSManagerForLayers* m_layers_slider{ nullptr }; - DoubleSlider::DSManagerForGcode* m_moves_slider { nullptr }; + DoubleSlider::DSForLayers* m_layers_slider{ nullptr }; + DoubleSlider::DSForGcode* m_moves_slider { nullptr }; public: enum class OptionType : unsigned int diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index 9befb33b87..a9e20a04c5 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -6,7 +6,9 @@ #include "ImGuiDoubleSlider.hpp" namespace Slic3r { -namespace GUI { +namespace DoubleSlider { + +using namespace GUI; const ImU32 tooltip_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT); const ImU32 thumb_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT); @@ -125,46 +127,47 @@ ImGuiControl::ImGuiControl( int lowerValue, bool use_lower_thumb) : m_selection(ssUndef), m_name(name), - m_lower_value(lowerValue), - m_higher_value (higherValue), - m_min_value(minValue), - m_max_value(maxValue), + m_lower_pos(lowerValue), + m_higher_pos (higherValue), + m_min_pos(minValue), + m_max_pos(maxValue), m_flags(flags), m_draw_lower_thumb(use_lower_thumb) { } -int ImGuiControl::GetActiveValue() const +int ImGuiControl::GetActivePos() const { - return m_selection == ssLower ? m_lower_value : - m_selection == ssHigher ? m_higher_value : -1; + return m_selection == ssLower ? m_lower_pos : + m_selection == ssHigher ? m_higher_pos : -1; } -void ImGuiControl::SetLowerValue(const int lower_val) +void ImGuiControl::SetLowerPos(const int lower_pos) { m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); + m_lower_pos = lower_pos; + correct_lower_pos(); } -void ImGuiControl::SetHigherValue(const int higher_val) +void ImGuiControl::SetHigherPos(const int higher_pos) { m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); + m_higher_pos = higher_pos; + correct_higher_pos(); } -void ImGuiControl::SetSelectionSpan(const int lower_val, const int higher_val) +void ImGuiControl::SetSelectionSpan(const int lower_pos, const int higher_pos) { - m_lower_value = std::max(lower_val, m_min_value); - m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); - if (m_lower_value < m_higher_value) + m_lower_pos = std::max(lower_pos, m_min_pos); + m_higher_pos = std::max(std::min(higher_pos, m_max_pos), m_lower_pos); + if (m_lower_pos < m_higher_pos) m_combine_thumbs = false; } -void ImGuiControl::SetMaxValue(const int max_value) +void ImGuiControl::SetMaxPos(const int max_pos) { - m_max_value = max_value; + m_max_pos = max_pos; + correct_higher_pos(); } void ImGuiControl::MoveActiveThumb(int delta) @@ -173,36 +176,36 @@ void ImGuiControl::MoveActiveThumb(int delta) m_selection = ssHigher; if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); + m_lower_pos -= delta; + correct_lower_pos(); } else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); + m_higher_pos -= delta; + correct_higher_pos(); } } -void ImGuiControl::correct_lower_value() +void ImGuiControl::correct_lower_pos() { - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; + if (m_lower_pos < m_min_pos) + m_lower_pos = m_min_pos; + else if (m_lower_pos > m_max_pos) + m_lower_pos = m_max_pos; - if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_combine_thumbs) { - m_higher_value = m_lower_value; + if ((m_lower_pos >= m_higher_pos && m_lower_pos <= m_max_pos) || m_combine_thumbs) { + m_higher_pos = m_lower_pos; } } -void ImGuiControl::correct_higher_value() +void ImGuiControl::correct_higher_pos() { - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; + if (m_higher_pos > m_max_pos) + m_higher_pos = m_max_pos; + else if (m_higher_pos < m_min_pos) + m_higher_pos = m_min_pos; - if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_combine_thumbs) { - m_lower_value = m_higher_value; + if ((m_higher_pos <= m_lower_pos && m_higher_pos >= m_min_pos) || m_combine_thumbs) { + m_lower_pos = m_higher_pos; } } @@ -211,17 +214,17 @@ void ImGuiControl::CombineThumbs(bool combine) m_combine_thumbs = combine; if (combine) { m_selection = ssHigher; - correct_higher_value(); + correct_higher_pos(); } else - ResetValues(); + ResetPositions(); } -void ImGuiControl::ResetValues() +void ImGuiControl::ResetPositions() { - SetLowerValue(m_min_value); - SetHigherValue(m_max_value); - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); + SetLowerPos(m_min_pos); + SetHigherPos(m_max_pos); + m_selection == ssLower ? correct_lower_pos() : correct_higher_pos(); } std::string ImGuiControl::get_label(int pos) const @@ -229,18 +232,18 @@ std::string ImGuiControl::get_label(int pos) const if (m_cb_get_label) return m_cb_get_label(pos); - if (pos >= m_max_value || pos < m_min_value) + if (pos >= m_max_pos || pos < m_min_pos) return "ErrVal"; return std::to_string(pos); } -float ImGuiControl::GetPositionFromValue(int value, const ImRect& rect) const +float ImGuiControl::GetPositionInRect(int pos, const ImRect& rect) const { - int v_min = m_min_value; - int v_max = m_max_value; + int v_min = m_min_pos; + int v_max = m_max_pos; - float pos_ratio = (v_max - v_min) != 0 ? ((float)(value - v_min) / (float)(v_max - v_min)) : 0.0f; + float pos_ratio = (v_max - v_min) != 0 ? ((float)(pos - v_min) / (float)(v_max - v_min)) : 0.0f; float thumb_pos; if (is_horizontal()) { thumb_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio; @@ -324,7 +327,7 @@ void ImGuiControl::draw_thumb(const ImVec2& center, bool mark/* = false*/) } } -void ImGuiControl::apply_regions(int higher_value, int lower_value, const ImRect& draggable_region) +void ImGuiControl::apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region) { ImVec2 mid = draggable_region.GetCenter(); float thumb_radius = m_draw_opts.thumb_radius(); @@ -338,18 +341,18 @@ void ImGuiControl::apply_regions(int higher_value, int lower_value, const ImRect ImRect(draggable_region.Min + ImVec2(0, thumb_radius), draggable_region.Max); // initialize the thumbs. - float higher_thumb_pos = GetPositionFromValue(higher_value, m_regions.higher_slideable_region); + float higher_thumb_pos = GetPositionInRect(higher_pos, m_regions.higher_slideable_region); m_regions.higher_thumb = is_horizontal() ? ImRect(higher_thumb_pos - thumb_radius, mid.y - thumb_radius, higher_thumb_pos + thumb_radius, mid.y + thumb_radius) : ImRect(mid.x - thumb_radius, higher_thumb_pos - thumb_radius, mid.x + thumb_radius, higher_thumb_pos + thumb_radius); - float lower_thumb_pos = GetPositionFromValue(lower_value, m_regions.lower_slideable_region); + float lower_thumb_pos = GetPositionInRect(lower_pos, m_regions.lower_slideable_region); m_regions.lower_thumb = is_horizontal() ? ImRect(lower_thumb_pos - thumb_radius, mid.y - thumb_radius, lower_thumb_pos + thumb_radius, mid.y + thumb_radius) : ImRect(mid.x - thumb_radius, lower_thumb_pos - thumb_radius, mid.x + thumb_radius, lower_thumb_pos + thumb_radius); } -void ImGuiControl::check_and_correct_thumbs(int* higher_value, int* lower_value) +void ImGuiControl::check_and_correct_thumbs(int* higher_pos, int* lower_pos) { if (!m_draw_lower_thumb || m_combine_thumbs) return; @@ -366,12 +369,12 @@ void ImGuiControl::check_and_correct_thumbs(int* higher_value, int* lower_value) if (m_selection == ssHigher) { m_regions.lower_thumb = m_regions.higher_thumb; m_regions.higher_thumb.TranslateX(thumb_radius); - *lower_value = *higher_value; + *lower_pos = *higher_pos; } else { m_regions.higher_thumb = m_regions.lower_thumb; m_regions.lower_thumb.TranslateX(-thumb_radius); - *higher_value = *lower_value; + *higher_pos = *lower_pos; } } } @@ -380,18 +383,18 @@ void ImGuiControl::check_and_correct_thumbs(int* higher_value, int* lower_value) if (m_selection == ssHigher) { m_regions.lower_thumb = m_regions.higher_thumb; m_regions.lower_thumb.TranslateY(thumb_radius); - *lower_value = *higher_value; + *lower_pos = *higher_pos; } else { m_regions.higher_thumb = m_regions.lower_thumb; m_regions.lower_thumb.TranslateY(-thumb_radius); - *higher_value = *lower_value; + *higher_pos = *lower_pos; } } } } -bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, +bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos, std::string& higher_label, std::string& lower_label, const ImVec2& pos, const ImVec2& size, float scale) { @@ -418,7 +421,7 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, } // set slideable regions and thumbs. - apply_regions(*higher_value, *lower_value, draggable_region); + apply_regions(*higher_pos, *lower_pos, draggable_region); // select and mark higher thumb by default if (m_selection == ssUndef) @@ -433,28 +436,28 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, ImGui::ItemHoverable(m_regions.lower_thumb, id) && context.IO.MouseClicked[0]) m_selection = ssLower; - // update thumb position and value - bool value_changed = false; + // update thumb position + bool pos_changed = false; if (m_selection == ssHigher) { - value_changed = behavior(id, m_regions.higher_slideable_region, m_min_value, m_max_value, - higher_value, &m_regions.higher_thumb, m_flags); + pos_changed = behavior(id, m_regions.higher_slideable_region, m_min_pos, m_max_pos, + higher_pos, &m_regions.higher_thumb, m_flags); } else if (m_draw_lower_thumb && !m_combine_thumbs) { - value_changed = behavior(id, m_regions.lower_slideable_region, m_min_value, m_max_value, - lower_value, &m_regions.lower_thumb, m_flags); + pos_changed = behavior(id, m_regions.lower_slideable_region, m_min_pos, m_max_pos, + lower_pos, &m_regions.lower_thumb, m_flags); } - // check thumbs values and correct them if needed - check_and_correct_thumbs(higher_value, lower_value); + // check thumbs poss and correct them if needed + check_and_correct_thumbs(higher_pos, lower_pos); const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region; const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb; bool show_move_label = false; ImRect mouse_pos_rc = active_thumb; - if (!value_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) { - behavior(id, slideable_region, m_min_value, m_max_value, - &m_mouse_pos_value, &mouse_pos_rc, m_flags, true); + if (!pos_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) { + behavior(id, slideable_region, m_min_pos, m_max_pos, + &m_mouse_pos, &mouse_pos_rc, m_flags, true); show_move_label = true; } @@ -491,9 +494,9 @@ bool ImGuiControl::draw_slider( int* higher_value, int* lower_value, // draw label on mouse move if (show_move_label) - draw_label(get_label_on_move(m_mouse_pos_value), mouse_pos_rc); + draw_label(get_label_on_move(m_mouse_pos), mouse_pos_rc); - return value_changed; + return pos_changed; } bool ImGuiControl::render() @@ -519,21 +522,21 @@ bool ImGuiControl::render() float scale = 1.f; - int higher_value = m_higher_value; - int lower_value = m_lower_value; - std::string higher_label = get_label(m_higher_value); - std::string lower_label = get_label(m_lower_value); - int temp_higher_value = m_higher_value; - int temp_lower_value = m_lower_value; + int higher_pos = m_higher_pos; + int lower_pos = m_lower_pos; + std::string higher_label = get_label(m_higher_pos); + std::string lower_label = get_label(m_lower_pos); + int temp_higher_pos = m_higher_pos; + int temp_lower_pos = m_lower_pos; - if (draw_slider(&higher_value, &lower_value, higher_label, lower_label, m_pos, m_size, scale)) { - if (temp_higher_value != higher_value) { - m_higher_value = higher_value; + if (draw_slider(&higher_pos, &lower_pos, higher_label, lower_label, m_pos, m_size, scale)) { + if (temp_higher_pos != higher_pos) { + m_higher_pos = higher_pos; if (m_combine_thumbs) - m_lower_value = m_higher_value; + m_lower_pos = m_higher_pos; } - if (temp_lower_value != lower_value) - m_lower_value = lower_value; + if (temp_lower_pos != lower_pos) + m_lower_pos = lower_pos; result = true; } diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index b2f2263707..b09f864acf 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -24,7 +24,7 @@ std::string to_string_with_precision(const T a_value, const int n = 2) } namespace Slic3r { -namespace GUI { +namespace DoubleSlider { enum SelectedSlider { ssUndef, @@ -45,25 +45,24 @@ public: ImGuiControl() {} ~ImGuiControl() {} - int GetMinValue() const { return m_min_value; } - int GetMaxValue() const { return m_max_value; } - int GetLowerValue() const { return m_lower_value; } - int GetHigherValue() const { return m_higher_value; } - int GetActiveValue() const; - float GetPositionFromValue(int value, const ImRect& rect) const; + int GetMinPos() const { return m_min_pos; } + int GetMaxPos() const { return m_max_pos; } + int GetLowerPos() const { return m_lower_pos; } + int GetHigherPos() const { return m_higher_pos; } + int GetActivePos() const; // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. - void SetLowerValue (const int lower_val); - void SetHigherValue(const int higher_val); - void SetSelectionSpan(const int lower_val, const int higher_val); + void SetLowerPos (const int lower_pos); + void SetHigherPos(const int higher_pos); + void SetSelectionSpan(const int lower_pos, const int higher_pos); - void SetMaxValue(const int max_value); + void SetMaxPos(const int max_pos); void CombineThumbs(bool combine); - void ResetValues(); + void ResetPositions(); - void SetPos(ImVec2 pos) { m_pos = pos; } - void SetSize(ImVec2 size) { m_size = size; } - void SetScale(float scale) { m_draw_opts.scale = scale; } + void SetCtrlPos(ImVec2 pos) { m_pos = pos; } + void SetCtrlSize(ImVec2 size) { m_size = size; } + void SetCtrlScale(float scale) { m_draw_opts.scale = scale; } void Init(const ImVec2& pos, const ImVec2& size, float scale) { m_pos = pos; m_size = size; @@ -76,16 +75,15 @@ public: bool IsCombineThumbs() const { return m_combine_thumbs; } bool IsActiveHigherThumb() const { return m_selection == ssHigher; } void MoveActiveThumb(int delta); + void ShowLowerThumb(bool show) { m_draw_lower_thumb = show; } void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); } + float GetPositionInRect(int pos, const ImRect& rect) const; + + bool IsRClickOnThumb() const { return m_rclick_on_selected_thumb; } bool is_horizontal() const { return !(m_flags & ImGuiSliderFlags_Vertical); } - bool is_lower_at_min() const { return m_lower_value == m_min_value; } - bool is_higher_at_max() const { return m_higher_value == m_max_value; } - bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } - bool is_rclick_on_thumb() const { return m_rclick_on_selected_thumb; } - bool render(); std::string get_label(int pos) const; @@ -130,11 +128,12 @@ private: ImGuiSliderFlags m_flags{ ImGuiSliderFlags_None }; bool m_is_shown{ true }; - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - int m_mouse_pos_value; + int m_min_pos; + int m_max_pos; + int m_lower_pos; + int m_higher_pos; + // slider's position of the mouse cursor + int m_mouse_pos; bool m_rclick_on_selected_thumb{ false }; @@ -150,24 +149,136 @@ private: std::function m_cb_draw_scroll_line { nullptr }; std::function m_cb_extra_draw { nullptr }; - void correct_lower_value(); - void correct_higher_value(); + void correct_lower_pos(); + void correct_higher_pos(); std::string get_label_on_move(int pos) const { return m_cb_get_label_on_move ? m_cb_get_label_on_move(pos) : get_label(pos); } - void apply_regions(int higher_value, int lower_value, const ImRect& draggable_region); - void check_and_correct_thumbs(int* higher_value, int* lower_value); + void apply_regions(int higher_pos, int lower_pos, const ImRect& draggable_region); + void check_and_correct_thumbs(int* higher_pos, int* lower_pos); void draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region); void draw_background(const ImRect& slideable_region); void draw_label(std::string label, const ImRect& thumb); void draw_thumb(const ImVec2& center, bool mark = false); - bool draw_slider(int* higher_value, int* lower_value, + bool draw_slider(int* higher_pos, int* lower_pos, std::string& higher_label, std::string& lower_label, const ImVec2& pos, const ImVec2& size, float scale = 1.0f); }; -} // GUI +// VatType = a typ of values, related to the each position in slider +template +class Manager +{ +public: + void Init( int lowerPos, + int higherPos, + int minPos, + int maxPos, + const std::string& name, + bool is_horizontal) + { + m_ctrl = ImGuiControl( lowerPos, higherPos, + minPos, maxPos, + is_horizontal ? 0 : ImGuiSliderFlags_Vertical, + name, !is_horizontal); + + m_ctrl.set_get_label_cb([this](int pos) {return get_label(pos); }); + }; + + Manager() {} + Manager(int lowerPos, + int higherPos, + int minPos, + int maxPos, + const std::string& name, + bool is_horizontal) + { + Init (lowerPos, higherPos, minPos, maxPos, name, is_horizontal); + } + ~Manager() {} + + int GetMinPos() const { return m_ctrl.GetMinPos(); } + int GetMaxPos() const { return m_ctrl.GetMaxPos(); } + int GetLowerPos() const { return m_ctrl.GetLowerPos(); } + int GetHigherPos()const { return m_ctrl.GetHigherPos(); } + + ValType GetMinValue() { return m_values.empty() ? static_cast(0) : m_values[GetMinPos()]; } + ValType GetMaxValue() { return m_values.empty() ? static_cast(0) : m_values[GetMaxPos()]; } + ValType GetLowerValue() { return m_values.empty() ? static_cast(0) : m_values[GetLowerPos()];} + ValType GetHigherValue() { return m_values.empty() ? static_cast(0) : m_values[GetHigherPos()]; } + + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetLowerPos(const int lower_pos) { + m_ctrl.SetLowerPos(lower_pos); + process_thumb_move(); + } + void SetHigherPos(const int higher_pos) { + m_ctrl.SetHigherPos(higher_pos); + process_thumb_move(); + } + void SetSelectionSpan(const int lower_pos, const int higher_pos) { + m_ctrl.SetSelectionSpan(lower_pos, higher_pos); + process_thumb_move(); + } + void SetMaxPos(const int max_pos) { + m_ctrl.SetMaxPos(max_pos); + process_thumb_move(); + } + + void SetSliderValues(const std::vector& values) { m_values = values; } + // values used to show thumb labels + void SetSliderAlternateValues(const std::vector& values) { m_alternate_values = values; } + + bool IsLowerAtMin() const { return m_ctrl.GetLowerPos() == m_ctrl.GetMinPos(); } + bool IsHigherAtMax() const { return m_ctrl.GetHigherPos() == m_ctrl.GetMaxPos(); } + + void Show(bool show = true) { m_ctrl.Show(show); } + void Hide() { m_ctrl.Show(false); } + void SetEmUnit(int em_unit) { m_em = em_unit; } + void ShowLowerThumb(bool show) { m_ctrl.ShowLowerThumb(show); } + + virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) = 0; + + void set_callback_on_thumb_move(std::function cb) { m_cb_thumb_move = cb; }; + + void move_current_thumb(const int delta) + { + m_ctrl.MoveActiveThumb(delta); + process_thumb_move(); + } + +protected: + + std::vector m_values; + std::vector m_alternate_values; + + ImGuiControl m_ctrl; + int m_em{ 10 }; + float m_scale{ 1.f }; + + std::string get_label(int pos) const { + if (m_values.empty()) + return std::to_string(pos); + if (pos >= m_values.size()) + return "ErrVal"; + return to_string_with_precision(static_cast(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos])); + } + + void process_thumb_move() { + if (m_cb_thumb_move) + m_cb_thumb_move(); + } + +private: + + std::function m_cb_thumb_move{ nullptr }; + +}; + + + +} // GUI } // Slic3r From 785ad4ce953cfb2ce6973d0d7444310e9839814c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Apr 2024 16:37:36 +0200 Subject: [PATCH 09/13] ImguiDoubleSlider: WIP: Functions which was related to manipulate with ticks are moved into TickCodeInfo * TickCodeInfo is renamed to TickCodesManager and moved to separate files * Added more callbacks to avoid use of wxWidgets * Added function for color picker rendering * For string formatting is used Slic3r::format instead of GUI::format (To avoid including wx/string) * All code related to DoubleSlider extracted from Slic3r name space --- resources/localization/list.txt | 1 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/DoubleSliderForGcode.cpp | 4 - src/slic3r/GUI/DoubleSliderForGcode.hpp | 4 - src/slic3r/GUI/DoubleSliderForLayers.cpp | 1275 +++++----------------- src/slic3r/GUI/DoubleSliderForLayers.hpp | 274 ++--- src/slic3r/GUI/GUI_Preview.cpp | 167 ++- src/slic3r/GUI/GUI_Preview.hpp | 14 +- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 14 +- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 13 +- src/slic3r/GUI/ImGuiPureWrap.cpp | 228 ++++ src/slic3r/GUI/ImGuiPureWrap.hpp | 4 + src/slic3r/GUI/ImGuiWrapper.cpp | 228 ---- src/slic3r/GUI/ImGuiWrapper.hpp | 3 - src/slic3r/GUI/TickCodesManager.cpp | 704 ++++++++++++ src/slic3r/GUI/TickCodesManager.hpp | 221 ++++ 16 files changed, 1677 insertions(+), 1479 deletions(-) create mode 100644 src/slic3r/GUI/TickCodesManager.cpp create mode 100644 src/slic3r/GUI/TickCodesManager.hpp diff --git a/resources/localization/list.txt b/resources/localization/list.txt index 9d8429f41c..77b7fe8c83 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -30,6 +30,7 @@ src/slic3r/GUI/ConfigManipulation.cpp src/slic3r/GUI/ConfigSnapshotDialog.cpp src/slic3r/GUI/ConfigWizard.cpp src/slic3r/GUI/DesktopIntegrationDialog.cpp +src/slic3r/GUI/TickCodesManager.cpp src/slic3r/GUI/DoubleSliderForLayers.cpp src/slic3r/GUI/Downloader.cpp src/slic3r/GUI/DownloaderFileGet.cpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index f877148a03..65ab160b91 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -254,6 +254,8 @@ set(SLIC3R_GUI_SOURCES GUI/Mouse3DController.hpp GUI/ImGuiDoubleSlider.cpp GUI/ImGuiDoubleSlider.hpp + GUI/TickCodesManager.cpp + GUI/TickCodesManager.hpp GUI/DoubleSliderForLayers.cpp GUI/DoubleSliderForLayers.hpp GUI/DoubleSliderForGcode.cpp diff --git a/src/slic3r/GUI/DoubleSliderForGcode.cpp b/src/slic3r/GUI/DoubleSliderForGcode.cpp index 65e8663b1d..5a4833a824 100644 --- a/src/slic3r/GUI/DoubleSliderForGcode.cpp +++ b/src/slic3r/GUI/DoubleSliderForGcode.cpp @@ -5,8 +5,6 @@ #include "DoubleSliderForGcode.hpp" -namespace Slic3r { - namespace DoubleSlider { static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar @@ -28,6 +26,4 @@ void DSForGcode::Render(const int canvas_width, const int canvas_height, float e } // DoubleSlider -} // Slic3r - diff --git a/src/slic3r/GUI/DoubleSliderForGcode.hpp b/src/slic3r/GUI/DoubleSliderForGcode.hpp index c830701637..c216fe3017 100644 --- a/src/slic3r/GUI/DoubleSliderForGcode.hpp +++ b/src/slic3r/GUI/DoubleSliderForGcode.hpp @@ -7,8 +7,6 @@ #include "ImGuiDoubleSlider.hpp" -namespace Slic3r { - namespace DoubleSlider { class DSForGcode : public Manager @@ -36,7 +34,5 @@ private: } // DoubleSlider; -} // Slic3r - #endif // slic3r_GUI_DoubleSliderForGcode_hpp_ diff --git a/src/slic3r/GUI/DoubleSliderForLayers.cpp b/src/slic3r/GUI/DoubleSliderForLayers.cpp index ded272e298..49fb7dae0f 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.cpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.cpp @@ -4,63 +4,28 @@ ///|/ #include "DoubleSliderForLayers.hpp" -#include "libslic3r/libslic3r.h" -#include "libslic3r/GCode.hpp" -#include "GUI.hpp" -#include "GUI_App.hpp" -#include "Plater.hpp" +#include "libslic3r/Utils.hpp" // -> get_time_dhms() +#include "libslic3r/format.hpp" // -> format() #include "I18N.hpp" -#include "ExtruderSequenceDialog.hpp" -#include "libslic3r/Print.hpp" -#include "libslic3r/AppConfig.hpp" -#include "GUI_Utils.hpp" -#include "MsgDialog.hpp" - -#include -#include -#include #include -#include -#include -#include "format.hpp" -#include "NotificationManager.hpp" - -#include "ImGuiPureWrap.hpp" +#include "ImGuiWrapper.hpp" +#include "imgui/imgui_internal.h" #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include -namespace Slic3r { - -using namespace GUI; +using namespace Slic3r; +using namespace CustomGCode; +using Slic3r::format; namespace DoubleSlider { -constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 -constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 - static const float VERTICAL_SLIDER_WIDTH = 105.0f; -bool equivalent_areas(const double& bottom_area, const double& top_area) -{ - return fabs(bottom_area - top_area) <= miscalculation; -} - -static std::string gcode(Type type) -{ - const PrintConfig& config = GUI::wxGetApp().plater()->fff_print().config(); // ! ys_FIXME - switch (type) { - case ColorChange: return config.color_change_gcode; - case PausePrint: return config.pause_print_gcode; - case Template: return config.template_custom_gcode; - default: return ""; - } -} - DSForLayers::DSForLayers( int lowerValue, int higherValue, int minValue, @@ -72,26 +37,12 @@ DSForLayers::DSForLayers( int lowerValue, is_osx = true; #endif //__WXOSX__ Init(lowerValue, higherValue, minValue, maxValue, "layers_slider", false); + m_ctrl.ShowLabelOnMouseMove(true); - m_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? into_u8(get_label(pos, ltEstimatedTime)) : ""; }); + m_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? get_label(pos, ltEstimatedTime) : ""; }); m_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); }); - m_ticks.set_pause_print_msg(_u8L("Place bearings in slots and resume printing")); - m_ticks.set_extruder_colors(&m_extruder_colors); -} - -int DSForLayers::get_tick_from_value(double value, bool force_lower_bound/* = false*/) -{ - std::vector::iterator it; - if (m_is_wipe_tower && !force_lower_bound) - it = std::find_if(m_values.begin(), m_values.end(), - [value](const double & val) { return fabs(value - val) <= epsilon(); }); - else - it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); - - if (it == m_values.end()) - return -1; - return int(it - m_values.begin()); + m_ticks.set_values(&m_values); } Info DSForLayers::GetTicksValues() const @@ -119,27 +70,18 @@ void DSForLayers::SetTicksValues(const Info& custom_gcode_per_print_z) return; } + if (m_cb_get_print) + m_ticks.set_print(m_cb_get_print()); + const bool was_empty = m_ticks.empty(); - m_ticks.ticks.clear(); - const std::vector& heights = custom_gcode_per_print_z.gcodes; - for (auto h : heights) { - int tick = get_tick_from_value(h.print_z); - if (tick >=0) - m_ticks.ticks.emplace(TickCode{ tick, h.type, h.extruder, h.color, h.extra }); - } + m_ticks.set_ticks(custom_gcode_per_print_z); if (!was_empty && m_ticks.empty()) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one process_ticks_changed(); - // init extruder sequence in respect to the extruders count - if (m_ticks.empty()) - m_extruders_sequence.init(m_extruder_colors.size()); update_draw_scroll_line_cb(); - - if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) - m_ticks.mode = custom_gcode_per_print_z.mode; } void DSForLayers::SetLayersTimes(const std::vector& layers_times, float total_time) @@ -155,7 +97,7 @@ void DSForLayers::SetLayersTimes(const std::vector& layers_times, float t // Erase duplicates values from m_values and save it to the m_layers_values // They will be used for show the correct estimated time for MM print, when "No sparce layer" is enabled // See https://github.com/prusa3d/PrusaSlicer/issues/6232 - if (m_is_wipe_tower && m_values.size() != m_layers_times.size()) { + if (m_ticks.is_wipe_tower && m_values.size() != m_layers_times.size()) { m_layers_values = m_values; sort(m_layers_values.begin(), m_layers_values.end()); m_layers_values.erase(unique(m_layers_values.begin(), m_layers_values.end()), m_layers_values.end()); @@ -170,7 +112,7 @@ void DSForLayers::SetLayersTimes(const std::vector& layers_times, float t void DSForLayers::SetLayersTimes(const std::vector& layers_times) { - m_is_wipe_tower = false; + m_ticks.is_wipe_tower = false; m_layers_times = layers_times; for (size_t i = 1; i < m_layers_times.size(); i++) m_layers_times[i] += m_layers_times[i - 1]; @@ -192,20 +134,20 @@ void DSForLayers::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_mode MultiAsSingle; if (!m_ticks.mode || (m_ticks.empty() && m_ticks.mode != m_mode)) m_ticks.mode = m_mode; - m_only_extruder = only_extruder; + + m_ticks.only_extruder_id = only_extruder; + m_ticks.is_wipe_tower = m_mode != SingleExtruder; if (m_mode != SingleExtruder) UseDefaultColors(false); - - m_is_wipe_tower = m_mode != SingleExtruder; } void DSForLayers::SetExtruderColors( const std::vector& extruder_colors) { - m_extruder_colors = extruder_colors; + m_ticks.colors = extruder_colors; } -bool DSForLayers::IsNewPrint(const std::string& idxs) +bool DSForLayers::is_new_print(const std::string& idxs) { if (idxs == "sla" || idxs == m_print_obj_idxs) return false; @@ -233,13 +175,11 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region) if (m_ticks.empty()) return; - ImGuiContext& context = *GImGui; - const ImVec2 tick_border = ImVec2(23.0f, 2.0f) * m_scale; // distance form center begin end const ImVec2 tick_size = ImVec2(19.0f, 11.0f) * m_scale; const float tick_width = 1.0f * m_scale; - const float icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; + const float icon_side = m_imgui->GetTextureCustomRect(ImGui::PausePrint)->Height; const float icon_offset = 0.5f * icon_side;; const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); @@ -286,7 +226,6 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region) if (tick_it == active_tick_it && m_allow_editing) { // delete tick if (render_button(ImGui::RemoveTick, ImGui::RemoveTickHovered, btn_label, icon_pos, m_ctrl.IsActiveHigherThumb() ? fiHigherThumb : fiLowerThumb, tick_it->tick)) { - Type type = tick_it->type; m_ticks.ticks.erase(tick_it); process_ticks_changed(); break; @@ -295,10 +234,10 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region) else if (m_draw_mode != dmRegular)// if we have non-regular draw mode, all ticks should be marked with error icon activate_this_tick = render_button(ImGui::ErrorTick, ImGui::ErrorTickHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); else if (tick_it->type == ColorChange || tick_it->type == ToolChange) { - if (m_ticks.is_conflict_tick(*tick_it, m_mode, m_only_extruder, m_values[tick_it->tick])) + if (m_ticks.is_conflict_tick(*tick_it, m_mode, m_values[tick_it->tick])) activate_this_tick = render_button(ImGui::ErrorTick, ImGui::ErrorTickHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); } - else if (tick_it->type == PausePrint) + else if (tick_it->type == CustomGCode::PausePrint) activate_this_tick = render_button(ImGui::PausePrint, ImGui::PausePrintHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); else activate_this_tick = render_button(ImGui::EditGCode, ImGui::EditGCodeHovered, btn_label, icon_pos, fiActionIcon, tick_it->tick); @@ -312,20 +251,22 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region) } } -inline int hex_to_int(const char c) -{ - return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - static std::array decode_color_to_float_array(const std::string color) { + auto hex_digit_to_int = [](const char c) { + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; + }; + // set alpha to 1.0f by default std::array ret = { 0, 0, 0, 1.0f }; const char* c = color.data() + 1; if (color.size() == 7 && color.front() == '#') { for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_to_int(*c++); - int digit2 = hex_to_int(*c++); + 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) / 255.0f; } @@ -333,6 +274,13 @@ static std::array decode_color_to_float_array(const std::string color) return ret; } +std::string encode_color_from_float_array(const std::array& color) +{ + char buffer[64]; + ::sprintf(buffer, "#%02X%02X%02X", int(color[0] * 255.0f), int(color[1] * 255.0f), int(color[2] * 255.0f)); + return std::string(buffer); +} + void DSForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) { if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) @@ -357,8 +305,8 @@ void DSForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideabl }; //draw main colored band - const int default_color_idx = m_mode == MultiAsSingle ? std::max(m_only_extruder - 1, 0) : 0; - std::arrayrgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]); + const int default_color_idx = m_mode == MultiAsSingle ? std::max(m_ticks.only_extruder_id - 1, 0) : 0; + std::arrayrgba = decode_color_to_float_array(m_ticks.colors[default_color_idx]); ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); draw_main_band(band_clr); @@ -378,8 +326,8 @@ void DSForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideabl { const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : tick_it->type == ToolChange ? - get_color_for_tool_change_tick(tick_it) : - get_color_for_color_change_tick(tick_it); + m_ticks.get_color_for_tool_change_tick(tick_it) : + m_ticks.get_color_for_color_change_tick(tick_it); if (!clr_str.empty()) { std::arrayrgba = decode_color_to_float_array(clr_str); @@ -400,67 +348,176 @@ void DSForLayers::render_menu() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 10.0f) * m_scale); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_scale); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, ImGui::GetStyle().ItemSpacing.y }); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); - std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); - int extruder_num = colors.size(); + if (m_ctrl.IsRClickOnThumb()) + ImGui::OpenPopup("slider_full_menu_popup"); + else if (m_show_just_color_change_menu) + ImGui::OpenPopup("slider_add_tick_menu_popup"); + else if (m_show_cog_menu) + ImGui::OpenPopup("cog_menu_popup"); - if (m_ctrl.IsRClickOnThumb()) { - ImGui::OpenPopup("slider_menu_popup"); - } - - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); - if (ImGui::BeginPopup("slider_menu_popup")) { - if ((!m_ctrl.IsActiveHigherThumb() && GetLowerValue() == 0.0) || - (m_ctrl.IsActiveHigherThumb() && GetHigherValue() == 0.0)) - { - menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, false); - } - else - { - if (menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) { - add_code_as_tick(ColorChange); - } - if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { - add_code_as_tick(PausePrint); - } - if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) { - add_code_as_tick(Custom); - } - if (!gcode(Template).empty()) { - if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) { - add_code_as_tick(Template); - } - } - } - - //BBS render this menu item only when extruder_num > 1 - if (extruder_num > 1) { - if (!m_can_change_color || m_draw_mode == dmSequentialFffPrint) { - begin_menu(_u8L("Change Filament").c_str(), false); - } - else if (begin_menu(_u8L("Change Filament").c_str())) { - for (int i = 0; i < extruder_num; i++) { - std::array rgba = decode_color_to_float_array(colors[i]); - ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); - if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1); - } - end_menu(); - } - } - ImGui::EndPopup(); - } - ImGui::PopStyleVar(1); + if (m_allow_editing) + render_add_tick_menu(); + render_cog_menu(); ImGui::PopStyleColor(1); - ImGui::PopStyleVar(3); + ImGui::PopStyleVar(4); + + ImGuiContext& context = *GImGui; + if (context.IO.MouseReleased[0]) { + m_show_just_color_change_menu = false; + m_show_cog_menu = false; + } +} + +void DSForLayers::render_add_tick_menu() +{ + if (ImGui::BeginPopup("slider_full_menu_popup")) { + if (m_mode == SingleExtruder) { + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) { + add_code_as_tick(ColorChange); + } + } + else + render_multi_extruders_menu(); + + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Add Pause").c_str(), "")) { + add_code_as_tick(CustomGCode::PausePrint); + } + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) { + add_code_as_tick(Custom); + } + if (!gcode(Template).empty() && + ImGuiPureWrap::menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) { + add_code_as_tick(Template); + } + + ImGui::EndPopup(); + return; + } + + const std::string longest_menu_name = format(_u8L("Add color change (%1%) for:"), gcode(ColorChange)); + + const ImVec2 label_size = ImGui::CalcTextSize(longest_menu_name.c_str(), NULL, true); + const ImRect active_thumb_rect = m_ctrl.GetActiveThumbRect(); + const ImVec2 pos = active_thumb_rect.GetCenter(); + + ImGui::SetNextWindowPos(ImVec2(pos.x - label_size.x - active_thumb_rect.GetWidth(), pos.y)); + + if (ImGui::BeginPopup("slider_add_tick_menu_popup")) { + render_multi_extruders_menu(); + ImGui::EndPopup(); + } +} + +void DSForLayers::render_multi_extruders_menu() +{ + std::vector colors; + if (m_cb_get_extruder_colors) + colors = m_cb_get_extruder_colors(); + + int extruders_cnt = colors.size(); + + if (extruders_cnt > 1) { + const int tick = m_ctrl.GetActivePos(); + + if (m_mode == MultiAsSingle) { + const std::string menu_name = _u8L("Change extruder"); + if (ImGuiPureWrap::begin_menu(menu_name.c_str())) { + std::array active_extruders = m_ticks.get_active_extruders_for_tick(tick, m_mode); + for (int i = 1; i <= extruders_cnt; i++) { + const bool is_active_extruder = i == active_extruders[0] || i == active_extruders[1]; + std::string item_name = format(_u8L("Extruder %d"), i); + if (is_active_extruder) + item_name += " (" + _u8L("active") + ")"; + + std::array rgba = decode_color_to_float_array(colors[i - 1]); + ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + if (ImGuiPureWrap::menu_item_with_icon(item_name.c_str(), "", ImVec2(14, 14) * m_scale, icon_clr, false, !is_active_extruder)) + add_code_as_tick(ToolChange, i); + } + ImGuiPureWrap::end_menu(); + } + } + + const std::string menu_name = format(_u8L("Add color change (%1%) for:"), gcode(ColorChange)); + if (ImGuiPureWrap::begin_menu(menu_name.c_str())) { + std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_values[tick]); + + for (int i = 1; i <= extruders_cnt; i++) { + const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder + used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); + std::string item_name = format(_u8L("Extruder %d"), i); + if (is_used_extruder) + item_name += " (" + _u8L("used") + ")"; + + if (ImGuiPureWrap::menu_item_with_icon(item_name.c_str(), "")) + add_code_as_tick(ColorChange, i); + } + ImGuiPureWrap::end_menu(); + } + } +} + +void DSForLayers::render_color_picker() +{ + ImGuiContext& context = *GImGui; + const std::string title = _u8L("Select color for color change"); + if (m_show_color_picker) { + + ImGuiPureWrap::set_next_window_pos(1200, 200, ImGuiCond_Always, 0.5f, 0.0f); + ImGuiPureWrap::begin(title, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse); + + ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoDragDrop; + + auto col = decode_color_to_float_array(m_selectable_color); + if (ImGui::ColorPicker4("color_picker", (float*)&col, misc_flags)) { + m_selectable_color = encode_color_from_float_array(col); + m_show_color_picker = false; + } + ImGuiPureWrap::end(); + } + + if (auto clr_pcr_win = ImGui::FindWindowByName(title.c_str()); clr_pcr_win && context.CurrentWindow != clr_pcr_win) + m_show_color_picker = false; +} + +void DSForLayers::render_cog_menu() +{ + const ImVec2 icon_sz = ImVec2(14, 14); + if (ImGui::BeginPopup("cog_menu_popup")) { + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Jump to height").c_str(), "Shift+G")) { + jump_to_value(); + } + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Show estimated print time on mouse moving").c_str(), "", icon_sz, 0, m_show_estimated_times)) { + m_show_estimated_times = !m_show_estimated_times; + } + if (m_mode == MultiAsSingle && m_draw_mode == dmRegular && + ImGuiPureWrap::menu_item_with_icon(_u8L("Set extruder sequence for the entire print").c_str(), "")) { + if (m_ticks.edit_extruder_sequence(m_ctrl.GetMaxPos(), m_mode)) + process_ticks_changed(); + } + if (m_allow_editing) { + if (ImGuiPureWrap::menu_item_with_icon(_u8L("Use default colors").c_str(), "", icon_sz, 0, m_ticks.used_default_colors())) { + UseDefaultColors(!m_ticks.used_default_colors()); + } + + if (m_mode != MultiExtruder && m_draw_mode == dmRegular && + ImGuiPureWrap::menu_item_with_icon(_u8L("Set auto color changes").c_str(), "")) { + auto_color_change(); + } + } + + ImGui::EndPopup(); + } } bool DSForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick /*= -1*/) { - float scale = (float)wxGetApp().em_unit() / 10.0f; - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowBorderSize, 0); ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); @@ -475,7 +532,6 @@ bool DSForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_h | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; - auto m_imgui = wxGetApp().imgui(); ImGuiPureWrap::set_next_window_pos(pos.x, pos.y, ImGuiCond_Always); std::string win_name = label_id + "##btn_win"; ImGuiPureWrap::begin(win_name, windows_flag); @@ -501,24 +557,23 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float return; m_scale = extra_scale * 0.1f * m_em; - const float action_btn_sz = wxGetApp().imgui()->GetTextureCustomRect(ImGui::DSRevert)->Height; - const float tick_icon_side = wxGetApp().imgui()->GetTextureCustomRect(ImGui::PausePrint)->Height; + const float action_btn_sz = m_imgui->GetTextureCustomRect(ImGui::DSRevert)->Height; + const float tick_icon_side = m_imgui->GetTextureCustomRect(ImGui::PausePrint)->Height; ImVec2 pos; - ImVec2 size; pos.x = canvas_width - VERTICAL_SLIDER_WIDTH * m_scale - tick_icon_side; pos.y = 1.f * action_btn_sz; if (m_allow_editing) pos.y += 2.f; - size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz); - m_ctrl.ShowLabelOnMouseMove(true); + + ImVec2 size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz); m_ctrl.Init(pos, size, m_scale); if (m_ctrl.render()) { // request one more frame if value was changes with mouse wheel if (GImGui->IO.MouseWheel != 0.0f) - wxGetApp().imgui()->set_requires_extra_frame(); + m_imgui->set_requires_extra_frame(); process_thumb_move(); } @@ -538,21 +593,25 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float ChangeOneLayerLock(); btn_pos.y += 1.2f * action_btn_sz; - if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) - show_cog_icon_context_menu(); + if (render_button(ImGui::DSSettings, ImGui::DSSettingsHovered, "settings", btn_pos, fiCogIcon)) { + m_show_cog_menu = true; + } if (m_draw_mode == dmSequentialFffPrint && m_ctrl.IsRClickOnThumb()) { std::string tooltip = _u8L("The sequential print is on.\n" "It's impossible to apply any custom G-code for objects printing sequentually."); ImGuiPureWrap::tooltip(tooltip, ImGui::GetFontSize() * 20.0f); } - else if (m_allow_editing) + else render_menu(); + + if (m_allow_editing) + render_color_picker(); } bool DSForLayers::is_wipe_tower_layer(int tick) const { - if (!m_is_wipe_tower || tick >= (int)m_values.size()) + if (!m_ticks.is_wipe_tower || tick >= (int)m_values.size()) return false; if (tick == 0 || (tick == (int)m_values.size() - 1 && m_values[tick] > m_values[tick - 1])) return false; @@ -580,36 +639,36 @@ static std::string short_and_splitted_time(const std::string& time) ::sscanf(time.c_str(), "%ds", &seconds); // Format the dhm time. - auto get_d = [days]() { return GUI::format(_u8L("%1%d"), days); }; - auto get_h = [hours]() { return GUI::format(_u8L("%1%h"), hours); }; - auto get_m = [minutes](){ return GUI::format(_u8L("%1%m"), minutes); }; - auto get_s = [seconds](){ return GUI::format(_u8L("%1%s"), seconds); }; + auto get_d = [days]() { return format(_u8L("%1%d"), days); }; + auto get_h = [hours]() { return format(_u8L("%1%h"), hours); }; + auto get_m = [minutes](){ return format(_u8L("%1%m"), minutes); }; + auto get_s = [seconds](){ return format(_u8L("%1%s"), seconds); }; if (days > 0) - return GUI::format("%1%%2%\n%3%", get_d(), get_h(), get_m()); + return format("%1%%2%\n%3%", get_d(), get_h(), get_m()); if (hours > 0) { if (hours < 10 && minutes < 10 && seconds < 10) - return GUI::format("%1%%2%%3%", get_h(), get_m(), get_s()); + return format("%1%%2%%3%", get_h(), get_m(), get_s()); if (hours > 10 && minutes > 10 && seconds > 10) - return GUI::format("%1%\n%2%\n%3%", get_h(), get_m(), get_s()); + return format("%1%\n%2%\n%3%", get_h(), get_m(), get_s()); if ((minutes < 10 && seconds > 10) || (minutes > 10 && seconds < 10)) - return GUI::format("%1%\n%2%%3%", get_h(), get_m(), get_s()); - return GUI::format("%1%%2%\n%3%", get_h(), get_m(), get_s()); + return format("%1%\n%2%%3%", get_h(), get_m(), get_s()); + return format("%1%%2%\n%3%", get_h(), get_m(), get_s()); } if (minutes > 0) { if (minutes > 10 && seconds > 10) - return GUI::format("%1%\n%2%", get_m(), get_s()); - return GUI::format("%1%%2%", get_m(), get_s()); + return format("%1%\n%2%", get_m(), get_s()); + return format("%1%%2%", get_m(), get_s()); } return get_s(); } -std::string DSForLayers::get_label(int pos, LabelType label_type/* = ltHeightWithLayer*/) const +std::string DSForLayers::get_label(int pos, LabelType label_type) const { const size_t value = pos; if (m_values.empty()) - return GUI::format("%1%", pos); + return format("%1%", pos); if (value >= m_values.size()) return "ErrVal"; @@ -635,59 +694,23 @@ std::string DSForLayers::get_label(int pos, LabelType label_type/* = ltHeightWit }; if (label_type == ltEstimatedTime) { - if (m_is_wipe_tower) { + if (m_ticks.is_wipe_tower) { size_t layer_number = get_layer_number(value, label_type); return (layer_number == size_t(-1) || layer_number == m_layers_times.size()) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); } return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; } - std::string str = GUI::format("%1$.2f", m_values[value]); + std::string str = format("%1$.2f", m_values[value]); if (label_type == ltHeight) return str; if (label_type == ltHeightWithLayer) { - size_t layer_number = m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); - return GUI::format("%1%\n(%2%)", str, layer_number); + size_t layer_number = m_ticks.is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); + return format("%1%\n(%2%)", str, layer_number); } return ""; } -std::string DSForLayers::get_color_for_tool_change_tick(std::set::const_iterator it) const -{ - const int current_extruder = it->extruder == 0 ? std::max(m_only_extruder, 1) : it->extruder; - - auto it_n = it; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->type == ColorChange && it_n->extruder == current_extruder) - return it_n->color; - } - - return m_extruder_colors[current_extruder-1]; // return a color for a specific extruder from the colors list -} - -std::string DSForLayers::get_color_for_color_change_tick(std::set::const_iterator it) const -{ - const int def_extruder = std::max(1, m_only_extruder); - auto it_n = it; - bool is_tool_change = false; - while (it_n != m_ticks.ticks.begin()) { - --it_n; - if (it_n->type == ToolChange) { - is_tool_change = true; - if (it_n->extruder == it->extruder) - return it->color; - break; - } - if (it_n->type == ColorChange && it_n->extruder == it->extruder) - return it->color; - } - if (!is_tool_change && it->extruder == def_extruder) - return it->color; - - return ""; -} - void DSForLayers::ChangeOneLayerLock() { m_ctrl.CombineThumbs(!m_ctrl.IsCombineThumbs()); @@ -775,7 +798,7 @@ std::string DSForLayers::get_tooltip(int tick/*=-1*/) for (size_t i = 1; i < MAX_LINES; ++i) { gcode += lines[i] + '\n'; } - gcode += "[" + into_u8(_L("continue")) + "]\n"; + gcode += "[" + _u8L("continue") + "]\n"; } boost::replace_all(gcode, "\n", "\n" + space); return gcode; @@ -783,19 +806,19 @@ std::string DSForLayers::get_tooltip(int tick/*=-1*/) tooltip += tick_code_it->type == ColorChange ? (m_mode == SingleExtruder ? - GUI::format(_L("Color change (\"%1%\")"), gcode(ColorChange)) : - GUI::format(_L("Color change (\"%1%\") for Extruder %2%"), gcode(ColorChange), tick_code_it->extruder)) : - tick_code_it->type == PausePrint ? - GUI::format(_L("Pause print (\"%1%\")"), gcode(PausePrint)) : + format(_u8L("Color change (\"%1%\")"), gcode(ColorChange)) : + format(_u8L("Color change (\"%1%\") for Extruder %2%"), gcode(ColorChange), tick_code_it->extruder)) : + tick_code_it->type == CustomGCode::PausePrint ? + format(_u8L("Pause print (\"%1%\")"), gcode(CustomGCode::PausePrint)) : tick_code_it->type == Template ? - GUI::format(_L("Custom template (\"%1%\")"), gcode(Template)) : + format(_u8L("Custom template (\"%1%\")"), gcode(Template)) : tick_code_it->type == ToolChange ? - GUI::format(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : + format(_u8L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : format_gcode(tick_code_it->extra);// tick_code_it->type == Custom // If tick is marked as a conflict (exclamation icon), // we should to explain why - ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); + ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_values[tick]); if (conflict != ctNone) tooltip += "\n\n" + _u8L("Note") + "! "; if (conflict == ctModeConflict) @@ -822,325 +845,38 @@ std::string DSForLayers::get_tooltip(int tick/*=-1*/) return tooltip; } -void DSForLayers::append_change_extruder_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) { - std::array active_extruders = get_active_extruders_for_tick(m_ctrl.GetActivePos()); - - std::vector icons = get_extruder_color_icons(true); - - wxMenu* change_extruder_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) { - const bool is_active_extruder = i == active_extruders[0] || i == active_extruders[1]; - const wxString item_name = wxString::Format(_L("Extruder %d"), i) + - (is_active_extruder ? " (" + _L("active") + ")" : ""); - - if (m_mode == MultiAsSingle) - append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(ToolChange, i); }, icons[i-1], menu, - [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); - } - - const wxString change_extruder_menu_name = m_mode == MultiAsSingle ? - (switch_current_code ? _L("Switch code to Change extruder") : _L("Change extruder") ) : - _L("Change extruder (N/A)"); - - append_submenu(menu, change_extruder_menu, wxID_ANY, change_extruder_menu_name, _L("Use another extruder"), - active_extruders[1] > 0 ? "edit_uni" : "change_extruder", - [this]() {return m_mode == MultiAsSingle /*&& !GUI::wxGetApp().obj_list()->has_paint_on_segmentation()*/; }, GUI::wxGetApp().plater()); // !ysFIXME has_paint_on_segmentation - } -} - -void DSForLayers::append_add_color_change_menu_item(wxMenu* menu, bool switch_current_code/* = false*/) -{ - const int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); - if (extruders_cnt > 1) { - int tick = m_ctrl.GetActivePos(); - std::set used_extruders_for_tick = m_ticks.get_used_extruders_for_tick(tick, m_only_extruder, m_values[tick]); - - wxMenu* add_color_change_menu = new wxMenu(); - - for (int i = 1; i <= extruders_cnt; i++) { - const bool is_used_extruder = used_extruders_for_tick.empty() ? true : // #ys_FIXME till used_extruders_for_tick doesn't filled correct for mmMultiExtruder - used_extruders_for_tick.find(i) != used_extruders_for_tick.end(); - const wxString item_name = wxString::Format(_L("Extruder %d"), i) + - (is_used_extruder ? " (" + _L("used") + ")" : ""); - - append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(ColorChange, i); }, "", menu, - []() { return true; }, GUI::wxGetApp().plater()); - } - - const wxString menu_name = switch_current_code ? - format_wxstr(_L("Switch code to Color change (%1%) for:"), gcode(ColorChange)) : - format_wxstr(_L("Add color change (%1%) for:"), gcode(ColorChange)); - wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(*get_bmp_bundle("colorchange_add_m")); - } -} - void DSForLayers::UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } -// Get active extruders for tick. -// Means one current extruder for not existing tick OR -// 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick) -// Use those values to disable selection of active extruders -std::array DSForLayers::get_active_extruders_for_tick(int tick) const -{ - int default_initial_extruder = m_mode == MultiAsSingle ? std::max(1, m_only_extruder) : 1; - std::array extruders = { default_initial_extruder, -1 }; - if (m_ticks.empty()) - return extruders; - - auto it = m_ticks.ticks.lower_bound(TickCode{tick}); - - if (it != m_ticks.ticks.end() && it->tick == tick) // current tick exists - extruders[1] = it->extruder; - - while (it != m_ticks.ticks.begin()) { - --it; - if(it->type == ToolChange) { - extruders[0] = it->extruder; - break; - } - } - - return extruders; -} - -void DSForLayers::show_cog_icon_context_menu() -{ - wxMenu menu; - - append_menu_item(&menu, wxID_ANY, _L("Jump to height") + " (Shift+G)", "", - [this](wxCommandEvent&) { jump_to_value(); }, "", & menu); - - if (m_allow_editing) - append_menu_check_item(&menu, wxID_ANY, _L("Use default colors"), _L("Use default colors on color change"), - [this](wxCommandEvent&) { - UseDefaultColors(!m_ticks.used_default_colors()); }, &menu, - []() { return true; }, [this]() { return m_ticks.used_default_colors(); }, GUI::wxGetApp().plater()); - - append_menu_check_item(&menu, wxID_ANY, _L("Show estimated print time on mouse moving"), _L("Show estimated print time on the ruler"), - [this](wxCommandEvent&) { m_show_estimated_times = !m_show_estimated_times; }, &menu, - []() { return true; }, [this]() { return m_show_estimated_times; }, GUI::wxGetApp().plater()); - - if (m_mode == MultiAsSingle && m_draw_mode == dmRegular) - append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "", - [this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu); - - if (GUI::wxGetApp().is_editor() && m_mode != MultiExtruder && m_draw_mode == dmRegular) - append_menu_item(&menu, wxID_ANY, _L("Set auto color changes"), "", - [this](wxCommandEvent&) { auto_color_change(); }, "", &menu); - - GUI::wxGetApp().plater()->PopupMenu(&menu); -} - -bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, std::function break_condition) -{ - double prev_area = area(object->get_layer(frst_layer_id)->lslices); - - bool detected = false; - for (size_t i = frst_layer_id+1; i < layers_cnt; i++) { - const Layer* layer = object->get_layer(i); - double cur_area = area(layer->lslices); - - // check for overhangs - if (check_overhangs && cur_area > prev_area && !equivalent_areas(prev_area, cur_area)) - break; - - // Check percent of the area decrease. - // This value have to be more than min_delta_area and more then 10% - if ((prev_area - cur_area > min_delta_area) && (cur_area / prev_area < 0.9)) { - detected = true; - if (break_condition(layer)) - break; - } - - prev_area = cur_area; - } - return detected; -} - +// !ysFIXME draw with imgui void DSForLayers::auto_color_change() { - if (!m_ticks.empty()) { - wxString msg_text = _L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" + - _L("This action is not revertible.\nDo you want to proceed?"); - GUI::WarningDialog dialog(nullptr, msg_text, _L("Warning"), wxYES | wxNO); - if (dialog.ShowModal() == wxID_NO) - return; - m_ticks.ticks.clear(); + if (m_ticks.auto_color_change(m_mode)) { + update_draw_scroll_line_cb(); + process_ticks_changed(); } - - int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); -// int extruder = 2; - - const Print& print = GUI::wxGetApp().plater()->fff_print(); - for (auto object : print.objects()) { - // An object should to have at least 2 layers to apply an auto color change - if (object->layer_count() < 2) - continue; - - check_color_change(object, 1, object->layers().size(), false, [this, extruders_cnt](const Layer* layer) - { - int tick = get_tick_from_value(layer->print_z); - if (tick >= 0 && !m_ticks.has_tick(tick)) { - if (m_mode == SingleExtruder) { - m_ticks.set_default_colors(true); - m_ticks.add_tick(tick, ColorChange, 1, layer->print_z); - } - else { - int extruder = 2; - if (!m_ticks.empty()) { - auto it = m_ticks.ticks.end(); - it--; - extruder = it->extruder + 1; - if (extruder > extruders_cnt) - extruder = 1; - } - m_ticks.add_tick(tick, ToolChange, extruder, layer->print_z); - } - } - // allow max 3 auto color changes - return m_ticks.ticks.size() > 2; - }); - } - - if (m_ticks.empty()) - GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange); - update_draw_scroll_line_cb(); - - process_ticks_changed(); } - -static std::string get_new_color(const std::string& color) -{ - wxColour clr(color); - if (!clr.IsOk()) - clr = wxColour(0, 0, 0); // Don't set alfa to transparence - - auto data = new wxColourData(); - data->SetChooseFull(1); - data->SetColour(clr); - - wxColourDialog dialog(GUI::wxGetApp().GetTopWindow(), data); - dialog.CenterOnParent(); - if (dialog.ShowModal() == wxID_OK) - return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); - return ""; -} - -/* To avoid get an empty string from wxTextEntryDialog - * Let disable OK button, if TextCtrl is empty - * OR input value is our of range (min..max), when min a nd max are positive - * */ -static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, double max = -1.0) -{ - GUI::wxGetApp().UpdateDlgDarkUI(dlg); - - // detect TextCtrl and OK button - wxTextCtrl* textctrl {nullptr}; - wxWindowList& dlg_items = dlg->GetChildren(); - for (auto item : dlg_items) { - textctrl = dynamic_cast(item); - if (textctrl) - break; - } - - if (!textctrl) - return; - - textctrl->SetInsertionPointEnd(); - - wxButton* btn_OK = static_cast(dlg->FindWindowById(wxID_OK)); - btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl, min, max](wxUpdateUIEvent& evt) - { - bool disable = textctrl->IsEmpty(); - if (!disable && min >= 0.0 && max >= 0.0) { - double value = -1.0; - if (!textctrl->GetValue().ToDouble(&value)) // input value couldn't be converted to double - disable = true; - else - disable = value < min - epsilon() || value > max + epsilon(); // is input value is out of valid range ? - } - - evt.Enable(!disable); - }, btn_OK->GetId()); -} - -static std::string get_custom_code(const std::string& code_in, double height) -{ - wxString msg_text = _L("Enter custom G-code used on current layer") + ":"; - wxString msg_header = format_wxstr(_L("Custom G-code on current layer (%1% mm)."), height); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, - wxTextEntryDialogStyle | wxTE_MULTILINE); - upgrade_text_entry_dialog(&dlg); - - bool valid = true; - std::string value; - do { - if (dlg.ShowModal() != wxID_OK) - return ""; - - value = into_u8(dlg.GetValue()); - valid = true;// GUI::Tab::validate_custom_gcode("Custom G-code", value); // !ysFIXME validate_custom_gcode - } while (!valid); - return value; -} - -static std::string get_pause_print_msg(const std::string& msg_in, double height) -{ - wxString msg_text = _L("Enter short message shown on Printer display when a print is paused") + ":"; - wxString msg_header = format_wxstr(_L("Message for pause print on current layer (%1% mm)."), height); - - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), - wxTextEntryDialogStyle); - upgrade_text_entry_dialog(&dlg); - - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return ""; - - return into_u8(dlg.GetValue()); -} - -// draw with imgui static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode) { wxString msg_text = _L("Enter the height you want to jump to") + ":"; wxString msg_header = _L("Jump to height"); wxString msg_in = "";// GUI::double_to_string(active_value); - // get custom gcode - wxTextEntryDialog dlg(nullptr, msg_text, msg_header, msg_in, wxTextEntryDialogStyle); - upgrade_text_entry_dialog(&dlg, min_z, max_z); - - if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) - return -1.0; - - double value = -1.0; - return dlg.GetValue().ToDouble(&value) ? value : -1.0; + return -1.0; } void DSForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { const int tick = m_ctrl.GetActivePos(); - if ( !check_ticks_changed_event(type) ) + if (!m_ticks.check_ticks_changed_event(type, m_mode)) { + process_ticks_changed(); return; + } - if (type == ColorChange && gcode(ColorChange).empty()) - GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyColorChangeCode); - - const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_only_extruder); + const int extruder = selected_extruder > 0 ? selected_extruder : std::max(1, m_ticks.only_extruder_id); const auto it = m_ticks.ticks.find(TickCode{ tick }); bool was_ticks = m_ticks.empty(); @@ -1161,6 +897,7 @@ void DSForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) if (was_ticks != m_ticks.empty()) update_draw_scroll_line_cb(); + m_show_just_color_change_menu = false; process_ticks_changed(); } @@ -1172,52 +909,27 @@ void DSForLayers::add_current_tick() const int tick = m_ctrl.GetActivePos(); auto it = m_ticks.ticks.find(TickCode{ tick }); - if (it != m_ticks.ticks.end() || // this tick is already exist - !check_ticks_changed_event(m_mode == MultiAsSingle ? ToolChange : ColorChange)) + if (it != m_ticks.ticks.end()) // this tick is already exist return; + if (!m_ticks.check_ticks_changed_event(m_mode == MultiAsSingle ? ToolChange : ColorChange, m_mode)) { + process_ticks_changed(); + return; + } if (m_mode == SingleExtruder) add_code_as_tick(ColorChange); - else - render_menu(); - /* - { - wxMenu menu; - - if (m_mode == MultiAsSingle) - append_change_extruder_menu_item(&menu); - else - append_add_color_change_menu_item(&menu); - - wxPoint pos = wxDefaultPosition; - /* Menu position will be calculated from mouse click position, but... - * if function is called from keyboard (pressing "+"), we should to calculate it - * * / - if (call_from_keyboard) { - int width, height; - get_size(&width, &height); - - const wxCoord coord = 0.75 * (is_horizontal() ? height : width); - this->GetPosition(&width, &height); - - pos = is_horizontal() ? - wxPoint(get_position_from_value(tick), height + coord) : - wxPoint(width + coord, get_position_from_value(tick)); - } - - GUI::wxGetApp().plater()->PopupMenu(&menu, pos); + else { + m_show_just_color_change_menu = true; + m_imgui->set_requires_extra_frame(); } - */ } void DSForLayers::delete_current_tick() { auto it = m_ticks.ticks.find(TickCode{ m_ctrl.GetActivePos()}); - if (it == m_ticks.ticks.end() || - !check_ticks_changed_event(it->type)) + if (it == m_ticks.ticks.end()) // this tick doesn't exist return; - Type type = it->type; m_ticks.ticks.erase(it); process_ticks_changed(); } @@ -1228,11 +940,11 @@ void DSForLayers::edit_tick(int tick/* = -1*/) tick = m_ctrl.GetActivePos(); const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); - if (it == m_ticks.ticks.end() || - !check_ticks_changed_event(it->type)) + if (it == m_ticks.ticks.end()) // this tick doesn't exist return; - if (m_ticks.edit_tick(it, m_values[it->tick])) + if (!m_ticks.check_ticks_changed_event(it->type, m_mode) || + m_ticks.edit_tick(it, m_values[it->tick])) process_ticks_changed(); } @@ -1245,67 +957,6 @@ void DSForLayers::discard_all_thicks() process_ticks_changed(); } -void DSForLayers::edit_extruder_sequence() -{ - if (!check_ticks_changed_event(ToolChange)) - return; - - GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); - if (dlg.ShowModal() != wxID_OK) - return; - m_extruders_sequence = dlg.GetValue(); - - m_ticks.erase_all_ticks_with_code(ToolChange); - - const int extr_cnt = m_extruders_sequence.extruders.size(); - if (extr_cnt == 1) - return; - - int tick = 0; - double value = 0.0; - int extruder = -1; - - std::random_device rd; //Will be used to obtain a seed for the random number engine - std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() - std::uniform_int_distribution<> distrib(0, extr_cnt-1); - - while (tick <= m_ctrl.GetMaxPos()) - { - bool color_repetition = false; - if (m_extruders_sequence.random_sequence) { - int rand_extr = distrib(gen); - if (m_extruders_sequence.color_repetition) - color_repetition = rand_extr == extruder; - else - while (rand_extr == extruder) - rand_extr = distrib(gen); - extruder = rand_extr; - } - else { - extruder++; - if (extruder == extr_cnt) - extruder = 0; - } - - const int cur_extruder = m_extruders_sequence.extruders[extruder]; - - bool meaningless_tick = tick == 0.0 && cur_extruder == extruder; - if (!meaningless_tick && !color_repetition) - m_ticks.ticks.emplace(TickCode{tick, ToolChange,cur_extruder + 1, m_extruder_colors[cur_extruder]}); - - if (m_extruders_sequence.is_mm_intervals) { - value += m_extruders_sequence.interval_by_mm; - tick = get_tick_from_value(value, true); - if (tick < 0) - break; - } - else - tick += m_extruders_sequence.interval_by_layers; - } - - process_ticks_changed(); -} - void DSForLayers::jump_to_value() { double value = get_value_to_jump(m_values[m_ctrl.GetActivePos()], @@ -1313,7 +964,7 @@ void DSForLayers::jump_to_value() if (value < 0.0) return; - int tick_value = get_tick_from_value(value); + int tick_value = m_ticks.get_tick_from_value(value); if (m_ctrl.IsActiveHigherThumb()) SetHigherPos(tick_value); @@ -1321,402 +972,6 @@ void DSForLayers::jump_to_value() SetLowerPos(tick_value); } -bool DSForLayers::check_ticks_changed_event(Type type) -{ - if ( m_ticks.mode == m_mode || - (type != ColorChange && type != ToolChange) || - (m_ticks.mode == SingleExtruder && m_mode == MultiAsSingle) || // All ColorChanges will be applied for 1st extruder - (m_ticks.mode == MultiExtruder && m_mode == MultiAsSingle) ) // Just mark ColorChanges for all unused extruders - return true; - - if ((m_ticks.mode == SingleExtruder && m_mode == MultiExtruder ) || - (m_ticks.mode == MultiExtruder && m_mode == SingleExtruder) ) - { - if (!m_ticks.has_tick_with_code(ColorChange)) - return true; - - wxString message = (m_ticks.mode == SingleExtruder ? - _L("The last color change data was saved for a single extruder printing.") : - _L("The last color change data was saved for a multi extruder printing.") - ) + "\n" + - _L("Your current changes will delete all saved color changes.") + "\n\n\t" + - _L("Are you sure you want to continue?"); - - //wxMessageDialog msg(this, message, _L("Notice"), wxYES_NO); - GUI::MessageDialog msg(nullptr, message, _L("Notice"), wxYES_NO); - if (msg.ShowModal() == wxID_YES) { - m_ticks.erase_all_ticks_with_code(ColorChange); - process_ticks_changed(); - } - return false; - } - // m_ticks_mode == MultiAsSingle - if( m_ticks.has_tick_with_code(ToolChange) ) { - wxString message = m_mode == SingleExtruder ? ( - _L("The last color change data was saved for a multi extruder printing.") + "\n\n" + - _L("Select YES if you want to delete all saved tool changes, \n" - "NO if you want all tool changes switch to color changes, \n" - "or CANCEL to leave it unchanged.") + "\n\n\t" + - _L("Do you want to delete all saved tool changes?") - ): ( // MultiExtruder - _L("The last color change data was saved for a multi extruder printing with tool changes for whole print.") + "\n\n" + - _L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" + - _L("Are you sure you want to continue?") ) ; - - GUI::MessageDialog msg(nullptr, message, _L("Notice"), wxYES_NO | (m_mode == SingleExtruder ? wxCANCEL : 0)); - const int answer = msg.ShowModal(); - if (answer == wxID_YES) { - m_ticks.erase_all_ticks_with_code(ToolChange); - process_ticks_changed(); - } - else if (m_mode == SingleExtruder && answer == wxID_NO) { - m_ticks.switch_code(ToolChange, ColorChange); - process_ticks_changed(); - } - return false; - } - - return true; -} - - - - -// Get used extruders for tick. -// Means all extruders(tools) which will be used during printing from current tick to the end -std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode/* = Undef*/) const -{ - Mode e_mode = !force_mode ? mode : force_mode; - - if (e_mode == MultiExtruder) { - // #ys_FIXME: get tool ordering from _correct_ place - const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); - - if (tool_ordering.empty()) - return {}; - - std::set used_extruders; - - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools& lhs, double rhs) { return lhs.print_z < rhs; }); - for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { - const std::vector& extruders = it_layer_tools->extruders; - for (const auto& extruder : extruders) - used_extruders.emplace(extruder + 1); - } - - return used_extruders; - } - - const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder, 1) : 1; - if (ticks.empty() || e_mode == SingleExtruder) - return { default_initial_extruder }; - - std::set used_extruders; - - auto it_start = ticks.lower_bound(TickCode{ tick }); - auto it = it_start; - if (it == ticks.begin() && it->type == ToolChange && - tick != it->tick) // In case of switch of ToolChange to ColorChange, when tick exists, - // we shouldn't change color for extruder, which will be deleted - { - used_extruders.emplace(it->extruder); - if (tick < it->tick) - used_extruders.emplace(default_initial_extruder); - } - - while (it != ticks.begin()) { - --it; - if (it->type == ToolChange && tick != it->tick) { - used_extruders.emplace(it->extruder); - break; - } - } - - if (it == ticks.begin() && used_extruders.empty()) - used_extruders.emplace(default_initial_extruder); - - for (it = it_start; it != ticks.end(); ++it) - if (it->type == ToolChange && tick != it->tick) - used_extruders.emplace(it->extruder); - - return used_extruders; -} - -std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int extruder) -{ - auto opposite_one_color = [](const std::string& color) { - ColorRGB rgb; - decode_color(color, rgb); - return encode_color(opposite(rgb)); - }; - auto opposite_two_colors = [](const std::string& a, const std::string& b) { - ColorRGB rgb1; decode_color(a, rgb1); - ColorRGB rgb2; decode_color(b, rgb2); - return encode_color(opposite(rgb1, rgb2)); - }; - - if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) { - - if (ticks.empty()) - return opposite_one_color((*m_colors)[0]); - - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick); - if (before_tick_it == ticks.end()) { - while (before_tick_it != ticks.begin()) - if (--before_tick_it; before_tick_it->type == ColorChange) - break; - if (before_tick_it->type == ColorChange) - return opposite_one_color(before_tick_it->color); - - return opposite_one_color((*m_colors)[0]); - } - - if (before_tick_it == ticks.begin()) { - const std::string& frst_color = (*m_colors)[0]; - if (before_tick_it->type == ColorChange) - return opposite_two_colors(frst_color, before_tick_it->color); - - auto next_tick_it = before_tick_it; - while (next_tick_it != ticks.end()) - if (++next_tick_it; next_tick_it->type == ColorChange) - break; - if (next_tick_it->type == ColorChange) - return opposite_two_colors(frst_color, next_tick_it->color); - - return opposite_one_color(frst_color); - } - - std::string frst_color = ""; - if (before_tick_it->type == ColorChange) - frst_color = before_tick_it->color; - else { - auto next_tick_it = before_tick_it; - while (next_tick_it != ticks.end()) - if (++next_tick_it; next_tick_it->type == ColorChange) { - frst_color = next_tick_it->color; - break; - } - } - - while (before_tick_it != ticks.begin()) - if (--before_tick_it; before_tick_it->type == ColorChange) - break; - - if (before_tick_it->type == ColorChange) { - if (frst_color.empty()) - return opposite_one_color(before_tick_it->color); - - return opposite_two_colors(before_tick_it->color, frst_color); - } - - if (frst_color.empty()) - return opposite_one_color((*m_colors)[0]); - - return opposite_two_colors((*m_colors)[0], frst_color); - } - - std::string color = (*m_colors)[extruder - 1]; - - if (type == ColorChange) { - if (!ticks.empty()) { - auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); - while (before_tick_it != ticks.begin()) { - --before_tick_it; - if (before_tick_it->type == ColorChange && before_tick_it->extruder == extruder) { - color = before_tick_it->color; - break; - } - } - } - - color = get_new_color(color); - } - return color; -} - -bool TickCodeInfo::add_tick(const int tick, Type type, const int extruder, double print_z) -{ - std::string color; - std::string extra; - if (type == Custom) // custom Gcode - { - extra = get_custom_code(custom_gcode, print_z); - if (extra.empty()) - return false; - custom_gcode = extra; - } - else if (type == PausePrint) { - extra = get_pause_print_msg(pause_print_msg, print_z); - if (extra.empty()) - return false; - pause_print_msg = extra; - } - else { - color = get_color_for_tick(TickCode{ tick }, type, extruder); - if (color.empty()) - return false; - } - - ticks.emplace(TickCode{ tick, type, extruder, color, extra }); - return true; -} - -bool TickCodeInfo::edit_tick(std::set::iterator it, double print_z) -{ - // Save previously value of the tick before the call a Dialog from get_... functions, - // otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close - // and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941) - TickCode changed_tick = *it; - - std::string edited_value; - if (it->type == ColorChange) - edited_value = get_new_color(it->color); - else if (it->type == PausePrint) - edited_value = get_pause_print_msg(it->extra, print_z); - else - edited_value = get_custom_code(it->type == Template ? gcode(Template) : it->extra, print_z); - - if (edited_value.empty()) - return false; - - // Update iterator. For this moment its value can be invalid - if (it = ticks.find(changed_tick); it == ticks.end()) - return false; - - if (it->type == ColorChange) { - if (it->color == edited_value) - return false; - changed_tick.color = edited_value; - } - else if (it->type == Template) { - if (gcode(Template) == edited_value) - return false; - changed_tick.extra = edited_value; - changed_tick.type = Custom; - } - else if (it->type == Custom || it->type == PausePrint) { - if (it->extra == edited_value) - return false; - changed_tick.extra = edited_value; - } - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -void TickCodeInfo::switch_code(Type type_from, Type type_to) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) - if (it->type == type_from) { - TickCode tick = *it; - tick.type = type_to; - tick.extruder = 1; - ticks.erase(it); - it = ticks.emplace(tick).first; - } - else - ++it; -} - -bool TickCodeInfo::switch_code_for_tick(std::set::iterator it, Type type_to, const int extruder) -{ - const std::string color = get_color_for_tick(*it, type_to, extruder); - if (color.empty()) - return false; - - TickCode changed_tick = *it; - changed_tick.type = type_to; - changed_tick.extruder = extruder; - changed_tick.color = color; - - ticks.erase(it); - ticks.emplace(changed_tick); - - return true; -} - -void TickCodeInfo::erase_all_ticks_with_code(Type type) -{ - for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { - if (it->type == type) - it = ticks.erase(it); - else - ++it; - } -} - -bool TickCodeInfo::has_tick_with_code(Type type) -{ - for (const TickCode& tick : ticks) - if (tick.type == type) - return true; - - return false; -} - -bool TickCodeInfo::has_tick(int tick) -{ - return ticks.find(TickCode{ tick }) != ticks.end(); -} - -ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, Mode out_mode, int only_extruder, double print_z) -{ - if ((tick.type == ColorChange && ( - (mode == SingleExtruder && out_mode == MultiExtruder ) || - (mode == MultiExtruder && out_mode == SingleExtruder) )) || - (tick.type == ToolChange && - (mode == MultiAsSingle && out_mode != MultiAsSingle)) ) - return ctModeConflict; - - // check ColorChange tick - if (tick.type == ColorChange) { - // We should mark a tick as a "MeaninglessColorChange", - // if it has a ColorChange for unused extruder from current print to end of the print - std::set used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z, out_mode); - - if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) - return ctMeaninglessColorChange; - - // We should mark a tick as a "Redundant", - // if it has a ColorChange for extruder that has not been used before - if (mode == MultiAsSingle && tick.extruder != std::max(only_extruder, 1) ) - { - auto it = ticks.lower_bound( tick ); - if (it == ticks.begin() && it->type == ToolChange && tick.extruder == it->extruder) - return ctNone; - - while (it != ticks.begin()) { - --it; - if (it->type == ToolChange && tick.extruder == it->extruder) - return ctNone; - } - - return ctRedundant; - } - } - - // check ToolChange tick - if (mode == MultiAsSingle && tick.type == ToolChange) { - // We should mark a tick as a "MeaninglessToolChange", - // if it has a ToolChange to the same extruder - auto it = ticks.find(tick); - if (it == ticks.begin()) - return tick.extruder == std::max(only_extruder, 1) ? ctMeaninglessToolChange : ctNone; - - while (it != ticks.begin()) { - --it; - if (it->type == ToolChange) - return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone; - } - } - - return ctNone; -} - } // DoubleSlider -} // Slic3r - diff --git a/src/slic3r/GUI/DoubleSliderForLayers.hpp b/src/slic3r/GUI/DoubleSliderForLayers.hpp index e486371f6a..79a05c7653 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.hpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.hpp @@ -5,34 +5,25 @@ #ifndef slic3r_GUI_DoubleSliderForLayers_hpp_ #define slic3r_GUI_DoubleSliderForLayers_hpp_ -#include "libslic3r/CustomGCode.hpp" #include "ImGuiDoubleSlider.hpp" +#include "TickCodesManager.hpp" #include #include -class wxMenu; - namespace Slic3r { +class Print; -using namespace CustomGCode; -class PrintObject; -class Layer; +namespace GUI +{ +class ImGuiWrapper; +} +} + +using namespace Slic3r::CustomGCode; namespace DoubleSlider { -using namespace GUI; - -// return true when areas are mostly equivalent -bool equivalent_areas(const double& bottom_area, const double& top_area); - -// return true if color change was detected -bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, - // what to do with detected color change - // and return true when detection have to be desturbed - std::function break_condition); - - enum FocusedItem { fiNone, fiRevertIcon, @@ -46,15 +37,6 @@ enum FocusedItem { fiTick }; -enum ConflictType -{ - ctNone, - ctModeConflict, - ctMeaninglessColorChange, - ctMeaninglessToolChange, - ctRedundant -}; - enum DrawMode { dmRegular, @@ -69,111 +51,6 @@ enum LabelType ltEstimatedTime, }; -struct TickCode -{ - bool operator<(const TickCode& other) const { return other.tick > this->tick; } - bool operator>(const TickCode& other) const { return other.tick < this->tick; } - - int tick = 0; - Type type = ColorChange; - int extruder = 0; - std::string color; - std::string extra; -}; - -class TickCodeInfo -{ - std::string custom_gcode; - std::string pause_print_msg; - bool m_suppress_plus = false; - bool m_suppress_minus = false; - bool m_use_default_colors{ true }; - - std::vector* m_colors {nullptr}; - - std::string get_color_for_tick(TickCode tick, Type type, const int extruder); - -public: - std::set ticks {}; - Mode mode = Undef; - - bool empty() const { return ticks.empty(); } - void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } - - bool add_tick(const int tick, Type type, int extruder, double print_z); - bool edit_tick(std::set::iterator it, double print_z); - void switch_code(Type type_from, Type type_to); - bool switch_code_for_tick(std::set::iterator it, Type type_to, const int extruder); - void erase_all_ticks_with_code(Type type); - - bool has_tick_with_code(Type type); - bool has_tick(int tick); - ConflictType is_conflict_tick(const TickCode& tick, Mode out_mode, int only_extruder, double print_z); - - // Get used extruders for tick. - // Means all extruders(tools) which will be used during printing from current tick to the end - std::set get_used_extruders_for_tick(int tick, int only_extruder, double print_z, Mode force_mode = Undef) const; - - void suppress_plus (bool suppress) { m_suppress_plus = suppress; } - void suppress_minus(bool suppress) { m_suppress_minus = suppress; } - bool suppressed_plus () { return m_suppress_plus; } - bool suppressed_minus() { return m_suppress_minus; } - void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; } - bool used_default_colors() const { return m_use_default_colors; } - - void set_extruder_colors(std::vector* extruder_colors) { m_colors = extruder_colors; } -}; - - -struct ExtrudersSequence -{ - bool is_mm_intervals = true; - double interval_by_mm = 3.0; - int interval_by_layers = 10; - bool random_sequence { false }; - bool color_repetition { false }; - std::vector extruders = { 0 }; - - bool operator==(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals == this->is_mm_intervals ) && - (other.interval_by_mm == this->interval_by_mm ) && - (other.interval_by_layers == this->interval_by_layers ) && - (other.random_sequence == this->random_sequence ) && - (other.color_repetition == this->color_repetition ) && - (other.extruders == this->extruders ) ; - } - bool operator!=(const ExtrudersSequence& other) const - { - return (other.is_mm_intervals != this->is_mm_intervals ) || - (other.interval_by_mm != this->interval_by_mm ) || - (other.interval_by_layers != this->interval_by_layers ) || - (other.random_sequence != this->random_sequence ) || - (other.color_repetition != this->color_repetition ) || - (other.extruders != this->extruders ) ; - } - - void add_extruder(size_t pos, size_t extruder_id = size_t(0)) - { - extruders.insert(extruders.begin() + pos+1, extruder_id); - } - - void delete_extruder(size_t pos) - { - if (extruders.size() == 1) - return;// last item can't be deleted - extruders.erase(extruders.begin() + pos); - } - - void init(size_t extruders_count) - { - extruders.clear(); - for (size_t extruder = 0; extruder < extruders_count; extruder++) - extruders.push_back(extruder); - } -}; - - class DSForLayers : public Manager { public: @@ -194,17 +71,19 @@ public: void SetDrawMode(bool is_sla_print, bool is_sequential_print); - void SetManipulationMode(Mode mode) { m_mode = mode; } - Mode GetManipulationMode() const { return m_mode; } void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); - void SetExtruderColors(const std::vector& extruder_colors); - - void UseDefaultColors(bool def_colors_on); - bool IsNewPrint(const std::string& print_obj_idxs); void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; - void set_callback_on_ticks_changed(std::function cb) { m_cb_ticks_changed = cb; }; + // jump to selected layer + void jump_to_value(); + + // just for editor + + void SetExtruderColors(const std::vector& extruder_colors); + void UseDefaultColors(bool def_colors_on); + bool is_new_print(const std::string& print_obj_idxs); + void set_imgui_wrapper(Slic3r::GUI::ImGuiWrapper* imgui) { m_imgui = imgui; } // manipulation with slider from keyboard @@ -214,82 +93,107 @@ public: void delete_current_tick(); // process adding of auto color change void auto_color_change(); - // jump to selected layer - void jump_to_value(); + + void set_callback_on_ticks_changed(std::function cb) + { m_cb_ticks_changed = cb; }; + + void set_callback_on_check_gcode(std::function cb ) + { m_ticks.set_callback_on_check_gcode(cb); } + + void set_callback_on_get_extruder_colors(std::function()> cb) + { m_cb_get_extruder_colors = cb; } + + void set_callback_on_get_print (std::function cb) + { m_cb_get_print = cb; } + + void set_callback_on_empty_auto_color_change(std::function cb) + { m_ticks.set_callback_on_empty_auto_color_change(cb); } + + void set_callback_on_get_custom_code(std::function cb) + { m_ticks.set_callback_on_get_custom_code(cb); } + + void set_callback_on_get_pause_print_msg(std::function cb) + { m_ticks.set_callback_on_get_pause_print_msg(cb); } + + void set_callback_on_get_new_color(std::function cb) + { m_ticks.set_callback_on_get_new_color(cb); } + + void set_callback_on_show_info_msg(std::function cb) + { m_ticks.set_callback_on_show_info_msg(cb); } + + void set_callback_on_show_warning_msg(std::function cb) + { m_ticks.set_callback_on_show_warning_msg(cb); } + + void set_callback_on_get_extruders_cnt(std::function cb) + { m_ticks.set_callback_on_get_extruders_cnt(cb); } + + void set_callback_on_get_extruders_sequence(std::function cb) + { m_ticks.set_callback_on_get_extruders_sequence(cb); } + + std::string gcode(Type type) { return m_ticks.gcode(type); } private: - void add_code_as_tick(Type type, int selected_extruder = -1); - void edit_tick(int tick = -1); - void discard_all_thicks(); - void edit_extruder_sequence(); - void show_cog_icon_context_menu(); - - bool is_wipe_tower_layer(int tick) const; - - std::string get_label(int tick, LabelType label_type = ltHeightWithLayer) const; - - int get_tick_from_value(double value, bool force_lower_bound = false); - std::string get_tooltip(int tick = -1); - - std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; - std::string get_color_for_color_change_tick(std::set::const_iterator it) const; - - void process_ticks_changed() { - if (m_cb_ticks_changed) - m_cb_ticks_changed(); - } - - // Get active extruders for tick. - // Means one current extruder for not existing tick OR - // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) - // Use those values to disable selection of active extruders - std::array get_active_extruders_for_tick(int tick) const; - - bool check_ticks_changed_event(Type type); - - void append_change_extruder_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME ! - void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false); // ysFIXME ! - bool is_osx { false }; bool m_allow_editing { true }; - bool m_is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower bool m_show_estimated_times { false }; + bool m_show_cog_menu { false }; DrawMode m_draw_mode { dmRegular }; Mode m_mode { SingleExtruder }; FocusedItem m_focus { fiNone }; - int m_only_extruder { -1 }; - - std::string m_print_obj_idxs; + TickCodeManager m_ticks; + Slic3r::GUI::ImGuiWrapper* m_imgui { nullptr }; std::vector m_layers_times; std::vector m_layers_values; - std::vector m_extruder_colors; - TickCodeInfo m_ticks; + bool is_wipe_tower_layer(int tick) const; - ExtrudersSequence m_extruders_sequence; + std::string get_label(int tick, LabelType label_type) const; - std::function m_cb_ticks_changed{ nullptr }; + std::string get_tooltip(int tick = -1); - bool m_can_change_color{ true }; + void update_draw_scroll_line_cb(); // functions for extend rendering of m_ctrl void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); void render_menu(); + void render_cog_menu(); bool render_button(const wchar_t btn_icon, const wchar_t btn_icon_hovered, const std::string& label_id, const ImVec2& pos, FocusedItem focus, int tick = -1); - void update_draw_scroll_line_cb(); + void add_code_as_tick(Type type, int selected_extruder = -1); + void edit_tick(int tick = -1); + void discard_all_thicks(); + + std::string get_label(int pos) const override { return get_label(pos, ltHeightWithLayer); } + + void process_ticks_changed() { + if (m_cb_ticks_changed) + m_cb_ticks_changed(); + } + + bool m_show_just_color_change_menu { false }; + bool m_show_color_picker { false }; + bool m_close { false }; + + std::string m_print_obj_idxs; + std::string m_selectable_color; + + void render_add_tick_menu(); + void render_multi_extruders_menu(); + void render_color_picker(); + + std::function m_cb_ticks_changed { nullptr }; + std::function()> m_cb_get_extruder_colors { nullptr }; + std::function m_cb_get_print { nullptr }; }; } // DoubleSlider; -} // Slic3r - #endif // slic3r_GUI_DoubleSliderForLayers_hpp_ diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 07d2717c6a..b46c52f88f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -21,8 +21,10 @@ #include "libslic3r/PresetBundle.hpp" #include "DoubleSliderForGcode.hpp" #include "DoubleSliderForLayers.hpp" +#include "ExtruderSequenceDialog.hpp" #include "Plater.hpp" #include "MainFrame.hpp" +#include "MsgDialog.hpp" #include "format.hpp" #include @@ -34,6 +36,7 @@ #include #include #include +#include // this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421 #include "libslic3r/Print.hpp" @@ -258,11 +261,6 @@ Preview::~Preview() if (m_canvas_widget != nullptr) delete m_canvas_widget; - - if (m_layers_slider) - delete m_layers_slider; - if (m_moves_slider) - delete m_moves_slider; } void Preview::set_as_dirty() @@ -357,33 +355,163 @@ void Preview::on_size(wxSizeEvent& evt) Refresh(); } +/* To avoid get an empty string from wxTextEntryDialog + * Let disable OK button, if TextCtrl is empty + * */ +static void upgrade_text_entry_dialog(wxTextEntryDialog* dlg, double min = -1.0, double max = -1.0) +{ + GUI::wxGetApp().UpdateDlgDarkUI(dlg); + + // detect TextCtrl and OK button + wxWindowList& dlg_items = dlg->GetChildren(); + for (auto item : dlg_items) { + if (wxTextCtrl* textctrl = dynamic_cast(item)) { + textctrl->SetInsertionPointEnd(); + + wxButton* btn_OK = static_cast(dlg->FindWindowById(wxID_OK)); + btn_OK->Bind(wxEVT_UPDATE_UI, [textctrl](wxUpdateUIEvent& evt) { + evt.Enable(!textctrl->IsEmpty()); + }, btn_OK->GetId()); + + break; + } + } +} + void Preview::create_sliders() { // Layers Slider - m_layers_slider = new DoubleSlider::DSForLayers(0, 0, 0, 100, wxGetApp().is_editor()); + m_layers_slider = std::make_unique(0, 0, 0, 100, wxGetApp().is_editor()); m_layers_slider->SetEmUnit(wxGetApp().em_unit()); + m_layers_slider->set_imgui_wrapper(wxGetApp().imgui()); m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA, wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects")); m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } ); - m_layers_slider->set_callback_on_ticks_changed( [this]() -> void { - Model& model = wxGetApp().plater()->model(); - model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues(); - m_schedule_background_process(); + if (wxGetApp().is_editor()) { + m_layers_slider->set_callback_on_ticks_changed([this]() -> void { + Model& model = wxGetApp().plater()->model(); + model.custom_gcode_per_print_z = m_layers_slider->GetTicksValues(); + m_schedule_background_process(); - m_keep_current_preview_type = false; - reload_print(); - }); + m_keep_current_preview_type = false; + reload_print(); + }); + + m_layers_slider->set_callback_on_check_gcode([this](CustomGCode::Type type) -> void { + if (type == ColorChange && m_layers_slider->gcode(ColorChange).empty()) + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyColorChangeCode); + }); + + m_layers_slider->set_callback_on_empty_auto_color_change([]() -> void { + GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::EmptyAutoColorChange); + }); + + m_layers_slider->set_callback_on_get_extruder_colors([]() -> std::vector { + return wxGetApp().plater()->get_extruder_colors_from_plater_config(); + }); + + m_layers_slider->set_callback_on_get_print([]() -> const Print& { + return GUI::wxGetApp().plater()->fff_print(); + }); + + m_layers_slider->set_callback_on_get_custom_code([](const std::string& code_in, double height) -> std::string + { + wxString msg_text = _L("Enter custom G-code used on current layer") + ":"; + wxString msg_header = format_wxstr(_L("Custom G-code on current layer (%1% mm)."), height); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, code_in, + wxTextEntryDialogStyle | wxTE_MULTILINE); + upgrade_text_entry_dialog(&dlg); + + bool valid = true; + std::string value; + do { + if (dlg.ShowModal() != wxID_OK) + return ""; + + value = into_u8(dlg.GetValue()); + valid = true;// GUI::Tab::validate_custom_gcode("Custom G-code", value); // !ysFIXME validate_custom_gcode + } while (!valid); + return value; + }); + + m_layers_slider->set_callback_on_get_pause_print_msg([](const std::string& msg_in, double height) -> std::string + { + wxString msg_text = _L("Enter short message shown on Printer display when a print is paused") + ":"; + wxString msg_header = format_wxstr(_L("Message for pause print on current layer (%1% mm)."), height); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, from_u8(msg_in), + wxTextEntryDialogStyle); + upgrade_text_entry_dialog(&dlg); + + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return ""; + + return into_u8(dlg.GetValue()); + }); + + m_layers_slider->set_callback_on_get_new_color([](const std::string& color) -> std::string + { + wxColour clr(color); + if (!clr.IsOk()) + clr = wxColour(0, 0, 0); // Don't set alfa to transparence + + auto data = new wxColourData(); + data->SetChooseFull(1); + data->SetColour(clr); + + wxColourDialog dialog(GUI::wxGetApp().GetTopWindow(), data); + dialog.CenterOnParent(); + if (dialog.ShowModal() == wxID_OK) + return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); + return ""; + }); + + m_layers_slider->set_callback_on_show_info_msg([this](const std::string& message, int btns_flag) -> int + { + GUI::MessageDialog msg(this, from_u8(message), _L("Notice"), btns_flag); + int ret = msg.ShowModal(); + return ret == wxID_YES ? wxYES : + ret == wxID_NO ? wxNO : + ret == wxID_CANCEL ? wxCANCEL : -1; + }); + + m_layers_slider->set_callback_on_show_warning_msg([this](const std::string& message, int btns_flag) -> int + { + GUI::WarningDialog msg(this, from_u8(message), _L("Warning"), btns_flag); + int ret = msg.ShowModal(); + return ret == wxID_YES ? wxYES : + ret == wxID_NO ? wxNO : + ret == wxID_CANCEL ? wxCANCEL : -1; + }); + + m_layers_slider->set_callback_on_get_extruders_cnt([]() -> int + { + return GUI::wxGetApp().extruders_edited_cnt(); + }); + + m_layers_slider->set_callback_on_get_extruders_sequence([](DoubleSlider::ExtrudersSequence& extruders_sequence) -> bool + { + GUI::ExtruderSequenceDialog dlg(extruders_sequence); + if (dlg.ShowModal() != wxID_OK) + return false; + extruders_sequence = dlg.GetValue(); + return true; + }); + } // Move Gcode Slider - m_moves_slider = new DoubleSlider::DSForGcode(0, 0, 0, 100); + m_moves_slider = std::make_unique(0, 0, 0, 100); m_moves_slider->SetEmUnit(wxGetApp().em_unit()); - m_moves_slider->set_callback_on_thumb_move([this]() ->void { Preview::on_moves_slider_scroll_changed(); }); + m_moves_slider->set_callback_on_thumb_move([this]() ->void { on_moves_slider_scroll_changed(); }); // m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_sliders_from_canvas, this); @@ -514,7 +642,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee // Suggest the auto color change, if model looks like sign if (!color_change_already_exists && wxGetApp().app_config->get_bool("allow_auto_color_change") && - m_layers_slider->IsNewPrint(get_print_obj_idxs())) + m_layers_slider->is_new_print(get_print_obj_idxs())) { const Print& print = wxGetApp().plater()->fff_print(); @@ -805,8 +933,6 @@ void Preview::load_print_as_fff(bool keep_z_range) // the view type may have been changed by the call m_canvas->load_gcode_preview() gcode_view_type = m_canvas->get_gcode_view_type(); zs = m_canvas->get_gcode_layers_zs(); - if (!zs.empty()) - m_moves_slider->Show(); m_loaded = true; } else if (is_pregcode_preview) { @@ -815,12 +941,9 @@ void Preview::load_print_as_fff(bool keep_z_range) // the view type has been changed by the call m_canvas->load_gcode_preview() if (gcode_view_type == libvgcode::EViewType::ColorPrint && !color_print_values.empty()) m_canvas->set_gcode_view_type(gcode_view_type); - m_moves_slider->Hide(); zs = m_canvas->get_gcode_layers_zs(); } - else { - m_moves_slider->Hide(); - } + m_moves_slider->Show(gcode_preview_data_valid && !zs.empty()); if (!zs.empty() && !m_keep_current_preview_type) { const unsigned int number_extruders = wxGetApp().is_editor() ? diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 5118532965..b2dd52885c 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -20,6 +20,11 @@ class wxComboBox; class wxComboCtrl; class wxCheckBox; +namespace DoubleSlider { + class DSForGcode; + class DSForLayers; +}; + namespace Slic3r { class DynamicPrintConfig; @@ -27,11 +32,6 @@ class Print; class BackgroundSlicingProcess; class Model; -namespace DoubleSlider { - class DSForGcode; - class DSForLayers; -}; - namespace GUI { class GLCanvas3D; @@ -96,8 +96,8 @@ class Preview : public wxPanel bool m_loaded { false }; - DoubleSlider::DSForLayers* m_layers_slider{ nullptr }; - DoubleSlider::DSForGcode* m_moves_slider { nullptr }; + std::unique_ptr m_layers_slider{ nullptr }; + std::unique_ptr m_moves_slider { nullptr }; public: enum class OptionType : unsigned int diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index a9e20a04c5..f70af66e2b 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -5,11 +5,8 @@ #include "ImGuiDoubleSlider.hpp" -namespace Slic3r { namespace DoubleSlider { -using namespace GUI; - const ImU32 tooltip_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_GREY_LIGHT); const ImU32 thumb_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT); const ImU32 groove_bg_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); @@ -255,6 +252,11 @@ float ImGuiControl::GetPositionInRect(int pos, const ImRect& rect) const return thumb_pos; } +ImRect ImGuiControl::GetActiveThumbRect() const +{ + return m_selection == ssLower ? m_regions.lower_thumb : m_regions.higher_thumb; +} + void ImGuiControl::draw_scroll_line(const ImRect& scroll_line, const ImRect& slideable_region) { if (m_cb_draw_scroll_line) @@ -548,9 +550,5 @@ bool ImGuiControl::render() return result; } -//} // DoubleSlider - -} // GUI - -} // Slic3r +} // DoubleSlider diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index b09f864acf..db3318706f 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -23,7 +23,6 @@ std::string to_string_with_precision(const T a_value, const int n = 2) return std::move(out).str(); } -namespace Slic3r { namespace DoubleSlider { enum SelectedSlider { @@ -80,6 +79,7 @@ public: void ShowLabelOnMouseMove(bool show = true) { m_show_move_label = show; } ImRect GetGrooveRect() const { return m_draw_opts.groove(m_pos, m_size, is_horizontal()); } float GetPositionInRect(int pos, const ImRect& rect) const; + ImRect GetActiveThumbRect() const; bool IsRClickOnThumb() const { return m_rclick_on_selected_thumb; } @@ -192,7 +192,7 @@ public: int minPos, int maxPos, const std::string& name, - bool is_horizontal) + bool is_horizontal) { Init (lowerPos, higherPos, minPos, maxPos, name, is_horizontal); } @@ -257,10 +257,10 @@ protected: int m_em{ 10 }; float m_scale{ 1.f }; - std::string get_label(int pos) const { + virtual std::string get_label(int pos) const { if (m_values.empty()) return std::to_string(pos); - if (pos >= m_values.size()) + if (pos >= int(m_values.size())) return "ErrVal"; return to_string_with_precision(static_cast(m_alternate_values.empty() ? m_values[pos] : m_alternate_values[pos])); } @@ -276,10 +276,7 @@ private: }; - - -} // GUI -} // Slic3r +} // DoubleSlider #endif // slic3r_ImGUI_DoubleSlider_hpp_ diff --git a/src/slic3r/GUI/ImGuiPureWrap.cpp b/src/slic3r/GUI/ImGuiPureWrap.cpp index 8128602fd3..4b3111acd8 100644 --- a/src/slic3r/GUI/ImGuiPureWrap.cpp +++ b/src/slic3r/GUI/ImGuiPureWrap.cpp @@ -617,4 +617,232 @@ bool is_chars_in_ranges(const ImWchar *ranges, return true; } +bool begin_menu(const char* label, bool enabled) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None); + + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow; + + // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). + // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. + // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. + if (g.MenusIdSubmittedThisFrame.contains(id)) { + if (menu_is_open) + menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + else + g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values + return menu_is_open; + } + + // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu + g.MenusIdSubmittedThisFrame.push_back(id); + + ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + bool pressed; + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && + (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, + // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). + // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { + // Menu inside an horizontal menu bar + // Selectable extend their highlight by half ItemSpacing in each direction. + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() + popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + float w = label_size.x; + pressed = /*selectable*/ImGui::Selectable(label, menu_is_open, + ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | + (!enabled ? ImGuiSelectableFlags_Disabled : 0), + ImVec2(w, 0.0f)); + ImGui::PopStyleVar(); + window->DC.CursorPos.x += IM_FLOOR( + style.ItemSpacing.x * + (-1.0f + + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else { + // Menu inside a menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w); + pressed = /*selectable*/ImGui::Selectable(label, menu_is_open, + ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | + ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), + ImVec2(min_w, 0.0f)); + ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); + ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + } + + const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id); + if (menuset_is_open) g.NavWindow = backed_nav_window; + + bool want_open = false; + bool want_close = false; + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_toward_other_child_menu = false; + + ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? + g.OpenPopupStack[g.BeginPopupStack.Size].Window : + NULL; + if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { + // FIXME-DPI: Values should be derived from a master "scale" factor. + ImRect next_window_rect = child_menu_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + // GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + } + if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) + want_close = true; + + if (!menu_is_open && hovered && pressed) // Click to open + want_open = true; + else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open + want_open = true; + + if (g.NavActivateId == id) { + want_close = menu_is_open; + want_open = !menu_is_open; + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + { + want_open = true; + ImGui::NavMoveRequestCancel(); + } + } + else { + // Menu bar + if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others + { + want_open = true; + } + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + { + want_open = true; + ImGui::NavMoveRequestCancel(); + } + } + + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + ImGui::OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) ImGui::OpenPopup(label); + + if (menu_is_open) { + ImGui::SetNextWindowPos(popup_pos, + ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + else { + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + } + + return menu_is_open; +} + +void end_menu() +{ + ImGui::EndMenu(); +} + +bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (window->SkipItems) return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), + // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. + ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); + bool pressed; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { + // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. + float w = label_size.x; + window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); + pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f)); + ImGui::PopStyleVar(); + window->DC.CursorPos.x += IM_FLOOR( + style.ItemSpacing.x * + (-1.0f + + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else { + // Menu item inside a vertical menu + // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. + // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. + float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f; + float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w); + pressed = /*selectable*/ImGui::Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + + if (icon_size.x != 0 && icon_size.y != 0) { + float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y; + float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2; + float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f; + ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y); + ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color); + } + + if (shortcut_w > 0.0f) { + ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + ImGui::PopStyleColor(); + } + if (selected) { + ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), + ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + } + } + + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + return pressed; +} + } // ImGuiPureWrap diff --git a/src/slic3r/GUI/ImGuiPureWrap.hpp b/src/slic3r/GUI/ImGuiPureWrap.hpp index 5af36ace83..ab4eccab1b 100644 --- a/src/slic3r/GUI/ImGuiPureWrap.hpp +++ b/src/slic3r/GUI/ImGuiPureWrap.hpp @@ -148,6 +148,10 @@ namespace ImGuiPureWrap bool is_chars_in_ranges(const ImWchar *ranges, const char *chars_ptr); bool is_char_in_ranges(const ImWchar *ranges, unsigned int letter); + bool begin_menu(const char* label, bool enabled = true); + void end_menu(); + bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true); + const ImVec4 COL_GREY_DARK = { 0.33f, 0.33f, 0.33f, 1.0f }; const ImVec4 COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; const ImVec4 COL_ORANGE_DARK = { 0.67f, 0.36f, 0.19f, 1.0f }; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 52a3d84494..25f3e342cd 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1552,234 +1552,6 @@ void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text) } } -bool begin_menu(const char* label, bool enabled) -{ - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None); - - // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow; - - // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). - // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. - // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used. - if (g.MenusIdSubmittedThisFrame.contains(id)) { - if (menu_is_open) - menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - else - g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values - return menu_is_open; - } - - // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu - g.MenusIdSubmittedThisFrame.push_back(id); - - ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && - (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, - // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). - // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. - ImVec2 popup_pos, pos = window->DC.CursorPos; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { - // Menu inside an horizontal menu bar - // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - float w = label_size.x; - pressed = selectable(label, menu_is_open, - ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | - (!enabled ? ImGuiSelectableFlags_Disabled : 0), - ImVec2(w, 0.0f)); - ImGui::PopStyleVar(); - window->DC.CursorPos.x += IM_FLOOR( - style.ItemSpacing.x * - (-1.0f + - 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else { - // Menu inside a menu - // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame - float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w); - pressed = selectable(label, menu_is_open, - ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | - ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), - ImVec2(min_w, 0.0f)); - ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); - ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); - } - - const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id); - if (menuset_is_open) g.NavWindow = backed_nav_window; - - bool want_open = false; - bool want_close = false; - if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - { - // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu - // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_toward_other_child_menu = false; - - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? - g.OpenPopupStack[g.BeginPopupStack.Size].Window : - NULL; - if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - // FIXME-DPI: Values should be derived from a master "scale" factor. - ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; - ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + - ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - // GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] - } - if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) - want_close = true; - - if (!menu_is_open && hovered && pressed) // Click to open - want_open = true; - else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open - want_open = true; - - if (g.NavActivateId == id) { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open - { - want_open = true; - ImGui::NavMoveRequestCancel(); - } - } - else { - // Menu bar - if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it - { - want_close = true; - want_open = menu_is_open = false; - } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others - { - want_open = true; - } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open - { - want_open = true; - ImGui::NavMoveRequestCancel(); - } - } - - if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' - want_close = true; - if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true); - - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); - - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. - ImGui::OpenPopup(label); - return false; - } - - menu_is_open |= want_open; - if (want_open) ImGui::OpenPopup(label); - - if (menu_is_open) { - ImGui::SetNextWindowPos(popup_pos, - ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. - menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - } - else { - g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values - } - - return menu_is_open; -} - -void end_menu() -{ - ImGui::EndMenu(); -} - -bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/) -{ - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->SkipItems) return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImVec2 pos = window->DC.CursorPos; - ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); - - // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), - // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. - ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); - bool pressed; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { - // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. - float w = label_size.x; - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f)); - ImGui::PopStyleVar(); - window->DC.CursorPos.x += IM_FLOOR( - style.ItemSpacing.x * - (-1.0f + - 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else { - // Menu item inside a vertical menu - // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. - // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f; - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w); - pressed = selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); - - if (icon_size.x != 0 && icon_size.y != 0) { - float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y; - float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2; - float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f; - ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y); - ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color); - } - - if (shortcut_w > 0.0f) { - ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); - ImGui::PopStyleColor(); - } - if (selected) { - //ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), - // ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); - } - } - - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); - return pressed; -} - } // namespace GUI diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 8cbec8b390..052eba2a3c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -153,9 +153,6 @@ namespace ImGuiPSWrap ColorRGBA from_ImU32(const ImU32& color); ColorRGBA from_ImVec4(const ImVec4& color); } -bool begin_menu(const char* label, bool enabled = true); -void end_menu(); -bool menu_item_with_icon(const char* label, const char* shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/TickCodesManager.cpp b/src/slic3r/GUI/TickCodesManager.cpp new file mode 100644 index 0000000000..c0bdfc563a --- /dev/null +++ b/src/slic3r/GUI/TickCodesManager.cpp @@ -0,0 +1,704 @@ +///|/ Copyright (c) Prusa Research 2020 - 2023 Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966 +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "TickCodesManager.hpp" + +#include "I18N.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/Color.hpp" + +using namespace Slic3r; +using namespace CustomGCode; + +namespace DoubleSlider { + +constexpr double min_delta_area = scale_(scale_(25)); // equal to 25 mm2 +constexpr double miscalculation = scale_(scale_(1)); // equal to 1 mm2 + +static const int YES = 0x00000002; // an analogue of wxYES +static const int NO = 0x00000008; // an analogue of wxNO +static const int CANCEL = 0x00000010; // an analogue of wxCANCEL + +bool equivalent_areas(const double& bottom_area, const double& top_area) +{ + return fabs(bottom_area - top_area) <= miscalculation; +} + +TickCodeManager::TickCodeManager() +{ + m_pause_print_msg = _u8L("Place bearings in slots and resume printing"); +} + +std::string TickCodeManager::gcode(CustomGCode::Type type) +{ + if (m_print) { + const Slic3r::PrintConfig& config = m_print->config(); + switch (type) { + case CustomGCode::ColorChange: return config.color_change_gcode; + case CustomGCode::PausePrint: return config.pause_print_gcode; + case CustomGCode::Template: return config.template_custom_gcode; + default: return std::string(); + } + } + return std::string(); +} + +int TickCodeManager::get_tick_from_value(double value, bool force_lower_bound/* = false*/) +{ + if (!m_values) + return -1; + std::vector::const_iterator it; + if (is_wipe_tower && !force_lower_bound) + it = std::find_if(m_values->begin(), m_values->end(), + [value](const double & val) { return fabs(value - val) <= epsilon(); }); + else + it = std::lower_bound(m_values->begin(), m_values->end(), value - epsilon()); + + if (it == m_values->end()) + return -1; + return int(it - m_values->begin()); +} + +void TickCodeManager::set_ticks(const Info& custom_gcode_per_print_z) +{ + ticks.clear(); + + const std::vector& heights = custom_gcode_per_print_z.gcodes; + for (auto h : heights) { + int tick = get_tick_from_value(h.print_z); + if (tick >=0) + ticks.emplace(TickCode{ tick, h.type, h.extruder, h.color, h.extra }); + } + + if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) + mode = custom_gcode_per_print_z.mode; +} + +// Get active extruders for tick. +// Means one current extruder for not existing tick OR +// 2 extruders - for existing tick (extruder before ToolChange and extruder of current existing tick) +// Use those values to disable selection of active extruders +std::array TickCodeManager::get_active_extruders_for_tick(int tick, Mode main_mode) const +{ + int default_initial_extruder = main_mode == MultiAsSingle ? std::max(1, only_extruder_id) : 1; + std::array extruders = { default_initial_extruder, -1 }; + if (empty()) + return extruders; + + auto it = ticks.lower_bound(TickCode{tick}); + + if (it != ticks.end() && it->tick == tick) // current tick exists + extruders[1] = it->extruder; + + while (it != ticks.begin()) { + --it; + if(it->type == ToolChange) { + extruders[0] = it->extruder; + break; + } + } + + return extruders; +} + +bool check_color_change(const PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, std::function break_condition) +{ + double prev_area = area(object->get_layer(frst_layer_id)->lslices); + + bool detected = false; + for (size_t i = frst_layer_id+1; i < layers_cnt; i++) { + const Layer* layer = object->get_layer(i); + double cur_area = area(layer->lslices); + + // check for overhangs + if (check_overhangs && cur_area > prev_area && !equivalent_areas(prev_area, cur_area)) + break; + + // Check percent of the area decrease. + // This value have to be more than min_delta_area and more then 10% + if ((prev_area - cur_area > min_delta_area) && (cur_area / prev_area < 0.9)) { + detected = true; + if (break_condition(layer)) + break; + } + + prev_area = cur_area; + } + return detected; +} + +bool TickCodeManager::auto_color_change(Mode main_mode) +{ + if (!m_print) + return false; + + if (!empty()) { + if (m_cb_show_warning_msg) { + std::string msg_text = _u8L("This action will cause deletion of all ticks on vertical slider.") + "\n\n" + + _u8L("This action is not revertible.\nDo you want to proceed?"); + if (m_cb_show_warning_msg(msg_text, YES | NO) == NO) + return false; + } + ticks.clear(); + } + + int extruders_cnt = m_cb_get_extruders_cnt ? m_cb_get_extruders_cnt() : 0; + + for (auto object : m_print->objects()) { + // An object should to have at least 2 layers to apply an auto color change + if (object->layer_count() < 2) + continue; + + check_color_change(object, 1, object->layers().size(), false, [this, extruders_cnt, main_mode](const Layer* layer) + { + int tick = get_tick_from_value(layer->print_z); + if (tick >= 0 && !has_tick(tick)) { + if (main_mode == SingleExtruder) { + set_default_colors(true); + add_tick(tick, ColorChange, 1, layer->print_z); + } + else { + int extruder = 2; + if (!empty()) { + auto it = ticks.end(); + it--; + extruder = it->extruder + 1; + if (extruder > extruders_cnt) + extruder = 1; + } + add_tick(tick, ToolChange, extruder, layer->print_z); + } + } + // allow max 3 auto color changes + return ticks.size() > 2; + }); + } + + if (empty() && m_cb_notify_empty_color_change) + m_cb_notify_empty_color_change(); + + return true; +} + +std::string TickCodeManager::get_new_color(const std::string& color) +{ + if (m_cb_get_new_color) + return m_cb_get_new_color(color); + return std::string(); +} + +std::string TickCodeManager::get_custom_code(const std::string& code_in, double height) +{ + if (m_cb_get_custom_code) + return m_cb_get_custom_code(code_in, height); + return std::string(); +} + +std::string TickCodeManager::get_pause_print_msg(const std::string& msg_in, double height) +{ + if (m_cb_get_pause_print_msg) + return m_cb_get_pause_print_msg(msg_in, height); + return std::string(); +} + +bool TickCodeManager::edit_extruder_sequence(const int max_tick, Mode main_mode) +{ + if (!check_ticks_changed_event(ToolChange, main_mode) || !m_cb_get_extruders_sequence) + return false; + + // init extruder sequence in respect to the extruders count + if (empty()) + m_extruders_sequence.init(colors.size()); + + if(!m_cb_get_extruders_sequence(m_extruders_sequence)) + return false; + + erase_all_ticks_with_code(ToolChange); + + const int extr_cnt = m_extruders_sequence.extruders.size(); + if (extr_cnt == 1) + return true; + + int tick = 0; + double value = 0.0; + int extruder = -1; + + std::random_device rd; //Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() + std::uniform_int_distribution<> distrib(0, extr_cnt-1); + + while (tick <= max_tick) + { + bool color_repetition = false; + if (m_extruders_sequence.random_sequence) { + int rand_extr = distrib(gen); + if (m_extruders_sequence.color_repetition) + color_repetition = rand_extr == extruder; + else + while (rand_extr == extruder) + rand_extr = distrib(gen); + extruder = rand_extr; + } + else { + extruder++; + if (extruder == extr_cnt) + extruder = 0; + } + + const int cur_extruder = m_extruders_sequence.extruders[extruder]; + + bool meaningless_tick = tick == 0.0 && cur_extruder == extruder; + if (!meaningless_tick && !color_repetition) + ticks.emplace(TickCode{tick, ToolChange,cur_extruder + 1, colors[cur_extruder]}); + + if (m_extruders_sequence.is_mm_intervals) { + value += m_extruders_sequence.interval_by_mm; + tick = get_tick_from_value(value, true); + if (tick < 0) + break; + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + return true; +} + +bool TickCodeManager::check_ticks_changed_event(Type type, Mode main_mode) +{ + if ( mode == main_mode || + (type != ColorChange && type != ToolChange) || + (mode == SingleExtruder && main_mode == MultiAsSingle) || // All ColorChanges will be applied for 1st extruder + (mode == MultiExtruder && main_mode == MultiAsSingle) ) // Just mark ColorChanges for all unused extruders + return true; + + if ((mode == SingleExtruder && main_mode == MultiExtruder ) || + (mode == MultiExtruder && main_mode == SingleExtruder) ) + { + if (!has_tick_with_code(ColorChange)) + return true; + + if (m_cb_show_info_msg) { + std::string message = (mode == SingleExtruder ? + _u8L("The last color change data was saved for a single extruder printing.") : + _u8L("The last color change data was saved for a multi extruder printing.") + ) + "\n" + + _u8L("Your current changes will delete all saved color changes.") + "\n\n\t" + + _u8L("Are you sure you want to continue?"); + + if ( m_cb_show_info_msg(message, YES | NO) == YES) + erase_all_ticks_with_code(ColorChange); + } + return false; + } + // m_ticks_mode == MultiAsSingle + if( has_tick_with_code(ToolChange) ) { + if (m_cb_show_info_msg) { + std::string message = main_mode == SingleExtruder ? ( + _u8L("The last color change data was saved for a multi extruder printing.") + "\n\n" + + _u8L("Select YES if you want to delete all saved tool changes, \n" + "NO if you want all tool changes switch to color changes, \n" + "or CANCEL to leave it unchanged.") + "\n\n\t" + + _u8L("Do you want to delete all saved tool changes?") + ): ( // MultiExtruder + _u8L("The last color change data was saved for a multi extruder printing with tool changes for whole print.") + "\n\n" + + _u8L("Your current changes will delete all saved extruder (tool) changes.") + "\n\n\t" + + _u8L("Are you sure you want to continue?") ) ; + + const int answer = m_cb_show_info_msg(message, YES | NO | (main_mode == SingleExtruder ? CANCEL : 0)); + if (answer == YES) { + erase_all_ticks_with_code(ToolChange); + } + else if (main_mode == SingleExtruder && answer == NO) { + switch_code(ToolChange, ColorChange); + } + } + return false; + } + + if (m_cb_check_gcode_and_notify) + m_cb_check_gcode_and_notify(type); + + return true; +} + + + + +// Get used extruders for tick. +// Means all extruders(tools) which will be used during printing from current tick to the end +std::set TickCodeManager::get_used_extruders_for_tick(int tick, double print_z, Mode force_mode/* = Undef*/) const +{ + if (!m_print) + return {}; + + Mode e_mode = !force_mode ? mode : force_mode; + + if (e_mode == MultiExtruder) { + const ToolOrdering& tool_ordering = m_print->get_tool_ordering(); + + if (tool_ordering.empty()) + return {}; + + std::set used_extruders; + + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), print_z, [](const LayerTools& lhs, double rhs) { return lhs.print_z < rhs; }); + for (; it_layer_tools != tool_ordering.end(); ++it_layer_tools) { + const std::vector& extruders = it_layer_tools->extruders; + for (const auto& extruder : extruders) + used_extruders.emplace(extruder + 1); + } + + return used_extruders; + } + + const int default_initial_extruder = e_mode == MultiAsSingle ? std::max(only_extruder_id, 1) : 1; + if (ticks.empty() || e_mode == SingleExtruder) + return { default_initial_extruder }; + + std::set used_extruders; + + auto it_start = ticks.lower_bound(TickCode{ tick }); + auto it = it_start; + if (it == ticks.begin() && it->type == ToolChange && + tick != it->tick) // In case of switch of ToolChange to ColorChange, when tick exists, + // we shouldn't change color for extruder, which will be deleted + { + used_extruders.emplace(it->extruder); + if (tick < it->tick) + used_extruders.emplace(default_initial_extruder); + } + + while (it != ticks.begin()) { + --it; + if (it->type == ToolChange && tick != it->tick) { + used_extruders.emplace(it->extruder); + break; + } + } + + if (it == ticks.begin() && used_extruders.empty()) + used_extruders.emplace(default_initial_extruder); + + for (it = it_start; it != ticks.end(); ++it) + if (it->type == ToolChange && tick != it->tick) + used_extruders.emplace(it->extruder); + + return used_extruders; +} + +std::string TickCodeManager::get_color_for_tick(TickCode tick, Type type, const int extruder) +{ + auto opposite_one_color = [](const std::string& color) { + ColorRGB rgb; + decode_color(color, rgb); + return encode_color(opposite(rgb)); + }; + auto opposite_two_colors = [](const std::string& a, const std::string& b) { + ColorRGB rgb1; decode_color(a, rgb1); + ColorRGB rgb2; decode_color(b, rgb2); + return encode_color(opposite(rgb1, rgb2)); + }; + + if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) { + + if (ticks.empty()) + return opposite_one_color(colors[0]); + + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick); + if (before_tick_it == ticks.end()) { + while (before_tick_it != ticks.begin()) + if (--before_tick_it; before_tick_it->type == ColorChange) + break; + if (before_tick_it->type == ColorChange) + return opposite_one_color(before_tick_it->color); + + return opposite_one_color(colors[0]); + } + + if (before_tick_it == ticks.begin()) { + const std::string& frst_color = colors[0]; + if (before_tick_it->type == ColorChange) + return opposite_two_colors(frst_color, before_tick_it->color); + + auto next_tick_it = before_tick_it; + while (next_tick_it != ticks.end()) + if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange) + break; + if (next_tick_it != ticks.end() && next_tick_it->type == ColorChange) + return opposite_two_colors(frst_color, next_tick_it->color); + + return opposite_one_color(frst_color); + } + + std::string frst_color = ""; + if (before_tick_it->type == ColorChange) + frst_color = before_tick_it->color; + else { + auto next_tick_it = before_tick_it; + while (next_tick_it != ticks.end()) + if (++next_tick_it; next_tick_it != ticks.end() && next_tick_it->type == ColorChange) { + frst_color = next_tick_it->color; + break; + } + } + + while (before_tick_it != ticks.begin()) + if (--before_tick_it; before_tick_it->type == ColorChange) + break; + + if (before_tick_it->type == ColorChange) { + if (frst_color.empty()) + return opposite_one_color(before_tick_it->color); + + return opposite_two_colors(before_tick_it->color, frst_color); + } + + if (frst_color.empty()) + return opposite_one_color(colors[0]); + + return opposite_two_colors(colors[0], frst_color); + } + + std::string color = colors[extruder - 1]; + + if (type == ColorChange) { + if (!ticks.empty()) { + auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick ); + while (before_tick_it != ticks.begin()) { + --before_tick_it; + if (before_tick_it->type == ColorChange && before_tick_it->extruder == extruder) { + color = before_tick_it->color; + break; + } + } + } + + color = get_new_color(color); + } + return color; +} + +bool TickCodeManager::add_tick(const int tick, Type type, const int extruder, double print_z) +{ + std::string color; + std::string extra; + if (type == Custom) // custom Gcode + { + extra = get_custom_code(m_custom_gcode, print_z); + if (extra.empty()) + return false; + m_custom_gcode = extra; + } + else if (type == PausePrint) { + extra = get_pause_print_msg(m_pause_print_msg, print_z); + if (extra.empty()) + return false; + m_pause_print_msg = extra; + } + else { + color = get_color_for_tick(TickCode{ tick }, type, extruder); + if (color.empty()) + return false; + } + + ticks.emplace(TickCode{ tick, type, extruder, color, extra }); + return true; +} + +bool TickCodeManager::edit_tick(std::set::iterator it, double print_z) +{ + // Save previously value of the tick before the call a Dialog from get_... functions, + // otherwise a background process can change ticks values and current iterator wouldn't be valid for the moment of a Dialog close + // and PS will crash (see https://github.com/prusa3d/PrusaSlicer/issues/10941) + TickCode changed_tick = *it; + + std::string edited_value; + if (it->type == ColorChange) + edited_value = get_new_color(it->color); + else if (it->type == PausePrint) + edited_value = get_pause_print_msg(it->extra, print_z); + else + edited_value = get_custom_code(it->type == Template ? gcode(Template) : it->extra, print_z); + + if (edited_value.empty()) + return false; + + // Update iterator. For this moment its value can be invalid + if (it = ticks.find(changed_tick); it == ticks.end()) + return false; + + if (it->type == ColorChange) { + if (it->color == edited_value) + return false; + changed_tick.color = edited_value; + } + else if (it->type == Template) { + if (gcode(Template) == edited_value) + return false; + changed_tick.extra = edited_value; + changed_tick.type = Custom; + } + else if (it->type == Custom || it->type == PausePrint) { + if (it->extra == edited_value) + return false; + changed_tick.extra = edited_value; + if (it->type == Template) + changed_tick.type = Custom; + } + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeManager::switch_code(Type type_from, Type type_to) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) + if (it->type == type_from) { + TickCode tick = *it; + tick.type = type_to; + tick.extruder = 1; + ticks.erase(it); + it = ticks.emplace(tick).first; + } + else + ++it; +} + +bool TickCodeManager::switch_code_for_tick(std::set::iterator it, Type type_to, const int extruder) +{ + const std::string color = get_color_for_tick(*it, type_to, extruder); + if (color.empty()) + return false; + + TickCode changed_tick = *it; + changed_tick.type = type_to; + changed_tick.extruder = extruder; + changed_tick.color = color; + + ticks.erase(it); + ticks.emplace(changed_tick); + + return true; +} + +void TickCodeManager::erase_all_ticks_with_code(Type type) +{ + for (auto it{ ticks.begin() }, end{ ticks.end() }; it != end; ) { + if (it->type == type) + it = ticks.erase(it); + else + ++it; + } +} + +bool TickCodeManager::has_tick_with_code(Type type) +{ + for (const TickCode& tick : ticks) + if (tick.type == type) + return true; + + return false; +} + +bool TickCodeManager::has_tick(int tick) +{ + return ticks.find(TickCode{ tick }) != ticks.end(); +} + +ConflictType TickCodeManager::is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z) +{ + if ((tick.type == ColorChange && ( + (mode == SingleExtruder && main_mode == MultiExtruder ) || + (mode == MultiExtruder && main_mode == SingleExtruder) )) || + (tick.type == ToolChange && + (mode == MultiAsSingle && main_mode != MultiAsSingle)) ) + return ctModeConflict; + + // check ColorChange tick + if (tick.type == ColorChange) { + // We should mark a tick as a "MeaninglessColorChange", + // if it has a ColorChange for unused extruder from current print to end of the print + std::set used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, print_z, main_mode); + + if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) + return ctMeaninglessColorChange; + + // We should mark a tick as a "Redundant", + // if it has a ColorChange for extruder that has not been used before + if (mode == MultiAsSingle && tick.extruder != std::max(only_extruder_id, 1) ) + { + auto it = ticks.lower_bound( tick ); + if (it == ticks.begin() && it->type == ToolChange && tick.extruder == it->extruder) + return ctNone; + + while (it != ticks.begin()) { + --it; + if (it->type == ToolChange && tick.extruder == it->extruder) + return ctNone; + } + + return ctRedundant; + } + } + + // check ToolChange tick + if (mode == MultiAsSingle && tick.type == ToolChange) { + // We should mark a tick as a "MeaninglessToolChange", + // if it has a ToolChange to the same extruder + auto it = ticks.find(tick); + if (it == ticks.begin()) + return tick.extruder == std::max(only_extruder_id, 1) ? ctMeaninglessToolChange : ctNone; + + while (it != ticks.begin()) { + --it; + if (it->type == ToolChange) + return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone; + } + } + + return ctNone; +} + +std::string TickCodeManager::get_color_for_tool_change_tick(std::set::const_iterator it) const +{ + const int current_extruder = it->extruder == 0 ? std::max(only_extruder_id, 1) : it->extruder; + + auto it_n = it; + while (it_n != ticks.begin()) { + --it_n; + if (it_n->type == ColorChange && it_n->extruder == current_extruder) + return it_n->color; + } + + return colors[current_extruder-1]; // return a color for a specific extruder from the colors list +} + +std::string TickCodeManager::get_color_for_color_change_tick(std::set::const_iterator it) const +{ + const int def_extruder = std::max(1, only_extruder_id); + auto it_n = it; + bool is_tool_change = false; + while (it_n != ticks.begin()) { + --it_n; + if (it_n->type == ToolChange) { + is_tool_change = true; + if (it_n->extruder == it->extruder) + return it->color; + break; + } + if (it_n->type == ColorChange && it_n->extruder == it->extruder) + return it->color; + } + if (!is_tool_change && it->extruder == def_extruder) + return it->color; + + return ""; +} + +} // DoubleSlider + + diff --git a/src/slic3r/GUI/TickCodesManager.hpp b/src/slic3r/GUI/TickCodesManager.hpp new file mode 100644 index 0000000000..4eb28dce0f --- /dev/null +++ b/src/slic3r/GUI/TickCodesManager.hpp @@ -0,0 +1,221 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_GUI_TickCodesManager_hpp_ +#define slic3r_GUI_TickCodesManager_hpp_ + +#include "libslic3r/CustomGCode.hpp" + +#include +#include + +using namespace Slic3r::CustomGCode; +namespace Slic3r { +class PrintObject; +class Print; +class Layer; +} + +namespace DoubleSlider { + +// return true when areas are mostly equivalent +bool equivalent_areas(const double& bottom_area, const double& top_area); + +// return true if color change was detected +bool check_color_change(const Slic3r::PrintObject* object, size_t frst_layer_id, size_t layers_cnt, bool check_overhangs, + // what to do with detected color change + // and return true when detection have to be desturbed + std::function break_condition); +enum ConflictType +{ + ctNone, + ctModeConflict, + ctMeaninglessColorChange, + ctMeaninglessToolChange, + ctRedundant +}; + +struct ExtrudersSequence +{ + bool is_mm_intervals = true; + double interval_by_mm = 3.0; + int interval_by_layers = 10; + bool random_sequence { false }; + bool color_repetition { false }; + std::vector extruders = { 0 }; + + bool operator==(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals == this->is_mm_intervals ) && + (other.interval_by_mm == this->interval_by_mm ) && + (other.interval_by_layers == this->interval_by_layers ) && + (other.random_sequence == this->random_sequence ) && + (other.color_repetition == this->color_repetition ) && + (other.extruders == this->extruders ) ; + } + bool operator!=(const ExtrudersSequence& other) const + { + return (other.is_mm_intervals != this->is_mm_intervals ) || + (other.interval_by_mm != this->interval_by_mm ) || + (other.interval_by_layers != this->interval_by_layers ) || + (other.random_sequence != this->random_sequence ) || + (other.color_repetition != this->color_repetition ) || + (other.extruders != this->extruders ) ; + } + + void add_extruder(size_t pos, size_t extruder_id = size_t(0)) + { + extruders.insert(extruders.begin() + pos+1, extruder_id); + } + + void delete_extruder(size_t pos) + { + if (extruders.size() == 1) + return;// last item can't be deleted + extruders.erase(extruders.begin() + pos); + } + + void init(size_t extruders_count) + { + extruders.clear(); + for (size_t extruder = 0; extruder < extruders_count; extruder++) + extruders.push_back(extruder); + } +}; + + +struct TickCode +{ + bool operator<(const TickCode& other) const { return other.tick > this->tick; } + bool operator>(const TickCode& other) const { return other.tick < this->tick; } + + int tick = 0; + Type type = ColorChange; + int extruder = 0; + std::string color; + std::string extra; +}; + + +class TickCodeManager +{ + std::string m_custom_gcode; + std::string m_pause_print_msg; + bool m_use_default_colors { true }; + + const Slic3r::Print* m_print{ nullptr }; + // pointer to the m_values from DSForLayers + const std::vector* m_values{ nullptr }; + + ExtrudersSequence m_extruders_sequence; + + bool has_tick_with_code(Type type); + bool has_tick(int tick); + + std::string get_color_for_tick(TickCode tick, Type type, const int extruder); + + std::string get_custom_code(const std::string& code_in, double height); + std::string get_pause_print_msg(const std::string& msg_in, double height); + std::string get_new_color(const std::string& color); + + std::function m_cb_notify_empty_color_change { nullptr }; + std::function m_cb_check_gcode_and_notify { nullptr }; + + std::function m_cb_get_custom_code { nullptr }; + std::function m_cb_get_pause_print_msg { nullptr }; + std::function m_cb_get_new_color { nullptr }; + + std::function m_cb_show_info_msg { nullptr }; + std::function m_cb_show_warning_msg { nullptr }; + std::function m_cb_get_extruders_cnt { nullptr }; + std::function m_cb_get_extruders_sequence { nullptr }; + +public: + + TickCodeManager(); + ~TickCodeManager() {} + std::set ticks {}; + Mode mode { Undef }; + bool is_wipe_tower { false }; //This flag indicates that there is multiple extruder print with wipe tower + int only_extruder_id{ -1 }; + + // colors per extruder + std::vector colors {}; + + bool empty() const { return ticks.empty(); } + + void set_ticks(const Info& custom_gcode_per_print_z); + + bool add_tick(const int tick, Type type, int extruder, double print_z); + bool edit_tick(std::set::iterator it, double print_z); + void switch_code(Type type_from, Type type_to); + bool switch_code_for_tick(std::set::iterator it, Type type_to, const int extruder); + void erase_all_ticks_with_code(Type type); + + ConflictType is_conflict_tick(const TickCode& tick, Mode main_mode, double print_z); + + int get_tick_from_value(double value, bool force_lower_bound = false); + + std::string gcode(Slic3r::CustomGCode::Type type); + + // Get used extruders for tick. + // Means all extruders(tools) which will be used during printing from current tick to the end + std::set get_used_extruders_for_tick(int tick, double print_z, Mode force_mode = Undef) const; + + // Get active extruders for tick. + // Means one current extruder for not existing tick OR + // 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick) + // Use those values to disable selection of active extruders + std::array get_active_extruders_for_tick(int tick, Mode main_mode) const; + + std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; + std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + + // true -> if manipulation with ticks with selected type and in respect to the main_mode (slider mode) is possible + // false -> otherwise + bool check_ticks_changed_event(Type type, Mode main_mode); + + // return true, if extruder sequence was changed + bool edit_extruder_sequence(const int max_tick, Mode main_mode); + + // return true, if auto color change was successfully processed + bool auto_color_change(Mode main_mode); + + void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; } + bool used_default_colors() const { return m_use_default_colors; } + + void set_print(const Slic3r::Print& print) { if (!m_print) m_print = &print; } + void set_values(const std::vector* values) { m_values = values; } + + void set_callback_on_empty_auto_color_change(std::function cb) + { m_cb_notify_empty_color_change = cb; } + + void set_callback_on_check_gcode(std::function cb ) + { m_cb_check_gcode_and_notify = cb; } + + void set_callback_on_get_custom_code(std::function cb) + { m_cb_get_custom_code = cb; } + + void set_callback_on_get_pause_print_msg(std::function cb) + { m_cb_get_pause_print_msg = cb; } + + void set_callback_on_get_new_color(std::function cb) + { m_cb_get_new_color = cb; } + + void set_callback_on_show_info_msg(std::function cb) + { m_cb_show_info_msg = cb; } + + void set_callback_on_show_warning_msg(std::function cb) + { m_cb_show_warning_msg = cb; } + + void set_callback_on_get_extruders_cnt(std::function cb) + { m_cb_get_extruders_cnt = cb; } + + void set_callback_on_get_extruders_sequence(std::function cb) + { m_cb_get_extruders_sequence = cb; } +}; + +} // DoubleSlider; + +#endif // slic3r_GUI_TickCodesManager_hpp_ From 8a78ab1ce8f39c4643709a3af7417117bd55d6b4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 2 Apr 2024 11:12:41 +0200 Subject: [PATCH 10/13] ImguiDoubleSlider: WIP: ImGui implemented "jump to layer" --- src/slic3r/GUI/DoubleSliderForLayers.cpp | 103 +++++++++++++++++++---- src/slic3r/GUI/DoubleSliderForLayers.hpp | 6 +- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/DoubleSliderForLayers.cpp b/src/slic3r/GUI/DoubleSliderForLayers.cpp index 49fb7dae0f..95d7a4ec52 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.cpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.cpp @@ -551,6 +551,70 @@ bool DSForLayers::render_button(const wchar_t btn_icon, const wchar_t btn_icon_h return ret; } +bool DSForLayers::render_jump_to_window(const ImVec2& pos, double* active_value, double min_z, double max_z) +{ + if (!m_show_get_jump_value) + return false; + + const std::string msg_text = _u8L("Enter the height you want to jump to") + ":"; + const std::string win_name = _u8L("Jump to height") + "##btn_win"; + const ImVec2 msg_size = ImGui::CalcTextSize(msg_text.c_str(), NULL, true); + + const float ctrl_pos_x = msg_size.x + 15 * m_scale; + const float ctrl_width = 50.f * m_scale; + + ImGui::SetNextWindowPos(pos, ImGuiCond_Always); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 12.0f, 8.0f }); + + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.13f, 0.13f, 0.13f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + + ImGuiWindowFlags windows_flag = ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoScrollWithMouse; + + ImGui::Begin(win_name.c_str(), &m_show_get_jump_value, windows_flag); + + ImGui::AlignTextToFramePadding(); + ImGui::Text("%s", msg_text.c_str()); + ImGui::SameLine(ctrl_pos_x); + ImGui::PushItemWidth(ctrl_width); + + ImGui::InputDouble("##jump_to", active_value, 0.0, 0.0, "%.2f", ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); + //check if Enter was pressed + bool enter_pressed = ImGui::IsItemDeactivatedAfterEdit(); + + //check out of range + bool disable_ok = *active_value < min_z || *active_value > max_z; + + ImGui::Text("%s", ""); + ImGui::SameLine(ctrl_pos_x); + + if (disable_ok) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); + } + + bool ok_pressed = ImGui::Button("OK##jump_to", ImVec2(ctrl_width, 0.f)); + + if (disable_ok) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } + + ImGui::End(); + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(3); + + return enter_pressed || ok_pressed; +} + void DSForLayers::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) { if (!m_ctrl.IsShown()) @@ -575,6 +639,9 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float if (GImGui->IO.MouseWheel != 0.0f) m_imgui->set_requires_extra_frame(); process_thumb_move(); + + // discard all getters dialogs + m_show_get_jump_value = false; } // draw action buttons @@ -605,6 +672,10 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float else render_menu(); + if (render_jump_to_window(ImVec2(0.5f * canvas_width, 0.5f*canvas_height), &m_jump_to_value, + m_values[m_ctrl.GetMinPos()], m_values[m_ctrl.GetMaxPos()])) + process_jump_to_value(); + if (m_allow_editing) render_color_picker(); } @@ -858,14 +929,6 @@ void DSForLayers::auto_color_change() process_ticks_changed(); } } -static double get_value_to_jump(double active_value, double min_z, double max_z, DrawMode mode) -{ - wxString msg_text = _L("Enter the height you want to jump to") + ":"; - wxString msg_header = _L("Jump to height"); - wxString msg_in = "";// GUI::double_to_string(active_value); - - return -1.0; -} void DSForLayers::add_code_as_tick(Type type, int selected_extruder/* = -1*/) { @@ -959,17 +1022,23 @@ void DSForLayers::discard_all_thicks() void DSForLayers::jump_to_value() { - double value = get_value_to_jump(m_values[m_ctrl.GetActivePos()], - m_values[m_ctrl.GetMinPos()], m_values[m_ctrl.GetMaxPos()], m_draw_mode); - if (value < 0.0) - return; + //Init "jump to value"; + m_show_get_jump_value = true; + m_jump_to_value = m_values[m_ctrl.GetActivePos()]; - int tick_value = m_ticks.get_tick_from_value(value); + m_imgui->set_requires_extra_frame(); +} - if (m_ctrl.IsActiveHigherThumb()) - SetHigherPos(tick_value); - else - SetLowerPos(tick_value); +void DSForLayers::process_jump_to_value() +{ + if (int tick_value = m_ticks.get_tick_from_value(m_jump_to_value, true); tick_value > 0.0) { + m_show_get_jump_value = false; + + if (m_ctrl.IsActiveHigherThumb()) + SetHigherPos(tick_value); + else + SetLowerPos(tick_value); + } } } // DoubleSlider diff --git a/src/slic3r/GUI/DoubleSliderForLayers.hpp b/src/slic3r/GUI/DoubleSliderForLayers.hpp index 79a05c7653..620c21c530 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.hpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.hpp @@ -168,6 +168,7 @@ private: void add_code_as_tick(Type type, int selected_extruder = -1); void edit_tick(int tick = -1); void discard_all_thicks(); + void process_jump_to_value(); std::string get_label(int pos) const override { return get_label(pos, ltHeightWithLayer); } @@ -177,14 +178,17 @@ private: } bool m_show_just_color_change_menu { false }; + bool m_show_get_jump_value { false }; bool m_show_color_picker { false }; - bool m_close { false }; + + double m_jump_to_value { 0.0 }; std::string m_print_obj_idxs; std::string m_selectable_color; void render_add_tick_menu(); void render_multi_extruders_menu(); + bool render_jump_to_window(const ImVec2& pos, double* active_value, double min_z, double max_z); void render_color_picker(); std::function m_cb_ticks_changed { nullptr }; From f90ea2e8be2f112d8e253621d7819109537ada68 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Apr 2024 21:07:08 +0200 Subject: [PATCH 11/13] ImguiDoubleSlider: Improvements and bug fixes * Discard all custom changes button - click area is out of icon * Properties info table expand button doesn't work - when pointer moves by mouse * When some custom g-code/ pause is added then icons aren't visible well --- resources/icons/edit_gcode.svg | 2 +- resources/icons/edit_gcode_f.svg | 2 +- resources/icons/error_tick.svg | 2 +- resources/icons/error_tick_f.svg | 2 +- resources/icons/pause_print.svg | 2 +- resources/icons/pause_print_f.svg | 2 +- src/slic3r/GUI/DoubleSliderForGcode.cpp | 4 ++-- src/slic3r/GUI/DoubleSliderForLayers.cpp | 10 +++++----- src/slic3r/GUI/ImGuiDoubleSlider.cpp | 6 +++--- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 9 +++++---- src/slic3r/GUI/ImGuiWrapper.cpp | 18 +++++++++--------- 11 files changed, 30 insertions(+), 29 deletions(-) diff --git a/resources/icons/edit_gcode.svg b/resources/icons/edit_gcode.svg index 694e106cc2..e2b7279ea7 100644 --- a/resources/icons/edit_gcode.svg +++ b/resources/icons/edit_gcode.svg @@ -4,7 +4,7 @@ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> - + - + - + diff --git a/resources/icons/error_tick_f.svg b/resources/icons/error_tick_f.svg index adf876e3c6..7e86945c83 100644 --- a/resources/icons/error_tick_f.svg +++ b/resources/icons/error_tick_f.svg @@ -3,7 +3,7 @@ - + diff --git a/resources/icons/pause_print.svg b/resources/icons/pause_print.svg index a905b1ea12..300331ff13 100644 --- a/resources/icons/pause_print.svg +++ b/resources/icons/pause_print.svg @@ -4,7 +4,7 @@ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> - + - + tick); //draw tick icon-buttons @@ -286,7 +286,7 @@ void DSForLayers::draw_colored_band(const ImRect& groove, const ImRect& slideabl if (m_ticks.empty() || m_draw_mode == dmSequentialFffPrint) return; - ImVec2 blank_padding = ImVec2(5.0f, 2.0f) * m_scale; + ImVec2 blank_padding = ImVec2(0.5f * m_ctrl.GetGrooveRect().GetWidth(), 2.0f * m_scale); float blank_width = 1.0f * m_scale; ImRect blank_rect = ImRect(groove.GetCenter().x - blank_width, groove.Min.y, groove.GetCenter().x + blank_width, groove.Max.y); @@ -627,7 +627,7 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float ImVec2 pos; pos.x = canvas_width - VERTICAL_SLIDER_WIDTH * m_scale - tick_icon_side; - pos.y = 1.f * action_btn_sz; + pos.y = 1.5f * action_btn_sz; if (m_allow_editing) pos.y += 2.f; @@ -648,13 +648,13 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float const float groove_center_x = m_ctrl.GetGrooveRect().GetCenter().x; - ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.25f * action_btn_sz); + ImVec2 btn_pos = ImVec2(groove_center_x - 0.5f * action_btn_sz, pos.y - 0.75f * action_btn_sz); if (!m_ticks.empty() && m_allow_editing && render_button(ImGui::DSRevert, ImGui::DSRevertHovered, "revert", btn_pos, fiRevertIcon)) discard_all_thicks(); - btn_pos.y += 0.1f * action_btn_sz + size.y; + btn_pos.y += 0.5f * action_btn_sz + size.y; const bool is_one_layer = m_ctrl.IsCombineThumbs(); if (render_button(is_one_layer ? ImGui::Lock : ImGui::Unlock, is_one_layer ? ImGui::LockHovered : ImGui::UnlockHovered, "one_layer", btn_pos, fiOneLayerIcon)) ChangeOneLayerLock(); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.cpp b/src/slic3r/GUI/ImGuiDoubleSlider.cpp index f70af66e2b..d78a47e634 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.cpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.cpp @@ -285,7 +285,7 @@ void ImGuiControl::draw_background(const ImRect& slideable_region) void ImGuiControl::draw_label(std::string label, const ImRect& thumb) { - if (label.empty()) + if (label.empty() || label == "ErrVal") return; const ImVec2 thumb_center = thumb.GetCenter(); @@ -313,9 +313,9 @@ void ImGuiControl::draw_label(std::string label, const ImRect& thumb) void ImGuiControl::draw_thumb(const ImVec2& center, bool mark/* = false*/) { - const float line_width = 2.0f * m_draw_opts.scale; - const float line_offset = 9.0f * m_draw_opts.scale; + const float line_width = 1.5f * m_draw_opts.scale; const float radius = m_draw_opts.thumb_radius(); + const float line_offset = 0.5f * radius; const float hexagon_angle = is_horizontal() ? 0.f : IM_PI * 0.5f; diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index db3318706f..97f7b00739 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -98,14 +98,14 @@ private: struct DrawOptions { float scale { 1.f }; // used for Retina on osx - ImVec2 dummy_sz() const { return ImVec2(24.0f, 44.0f) * scale; } + ImVec2 dummy_sz() const { return ImVec2(24.0f, 22.0f) * scale; } ImVec2 thumb_dummy_sz() const { return ImVec2(17.0f, 17.0f) * scale; } - ImVec2 groove_sz() const { return ImVec2(10.0f, 8.0f) * scale; } - ImVec2 draggable_region_sz()const { return ImVec2(40.0f, 19.0f) * scale; } + ImVec2 groove_sz() const { return ImVec2(4.0f, 4.0f) * scale; } + ImVec2 draggable_region_sz()const { return ImVec2(20.0f, 19.0f) * scale; } ImVec2 text_dummy_sz() const { return ImVec2(50.0f, 34.0f) * scale; } ImVec2 text_padding() const { return ImVec2( 5.0f, 2.0f) * scale; } - float thumb_radius() const { return 14.0f * scale; } + float thumb_radius() const { return 10.0f * scale; } float thumb_border() const { return 2.0f * scale; } float rounding() const { return 2.0f * scale; } @@ -235,6 +235,7 @@ public: void Show(bool show = true) { m_ctrl.Show(show); } void Hide() { m_ctrl.Show(false); } + bool IsShown() { return m_ctrl.IsShown(); } void SetEmUnit(int em_unit) { m_em = em_unit; } void ShowLowerThumb(bool show) { m_ctrl.ShowLowerThumb(show); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 25f3e342cd..61e8dd7fa9 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -75,15 +75,6 @@ static const std::map font_icons = { {ImGui::SnapMarker , "snap" }, {ImGui::HorizontalHide , "horizontal_hide" }, {ImGui::HorizontalShow , "horizontal_show" }, - - {ImGui::ErrorTick , "error_tick" }, - {ImGui::ErrorTickHovered , "error_tick_f" }, - {ImGui::PausePrint , "pause_print" }, - {ImGui::PausePrintHovered , "pause_print_f" }, - {ImGui::EditGCode , "edit_gcode" }, - {ImGui::EditGCodeHovered , "edit_gcode_f" }, - {ImGui::RemoveTick , "colorchange_del" }, - {ImGui::RemoveTickHovered , "colorchange_del_f" }, }; static const std::map font_icons_large = { @@ -134,6 +125,15 @@ static const std::map font_icons_medium = { {ImGui::DSRevertHovered , "undo_f" }, {ImGui::DSSettings , "cog" }, {ImGui::DSSettingsHovered , "cog_f" }, + + {ImGui::ErrorTick , "error_tick" }, + {ImGui::ErrorTickHovered , "error_tick_f" }, + {ImGui::PausePrint , "pause_print" }, + {ImGui::PausePrintHovered , "pause_print_f" }, + {ImGui::EditGCode , "edit_gcode" }, + {ImGui::EditGCodeHovered , "edit_gcode_f" }, + {ImGui::RemoveTick , "colorchange_del" }, + {ImGui::RemoveTickHovered , "colorchange_del_f" }, }; static const std::map font_icons_extra_large = { From b02e0e6fd1e1cd4441ef61a564e7f03440aa75d6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 22 Apr 2024 21:08:12 +0200 Subject: [PATCH 12/13] =?UTF-8?q?ImguiDoubleSlider:=20Notifications=20plac?= =?UTF-8?q?ement=C2=A0to=20avoid=20overlapping=20with=20sliders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/slic3r/GUI/GUI_Preview.cpp | 14 ++++++++++++++ src/slic3r/GUI/GUI_Preview.hpp | 2 ++ src/slic3r/GUI/ImGuiDoubleSlider.hpp | 6 +++++- src/slic3r/GUI/NotificationManager.cpp | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index b46c52f88f..4b61a0c65e 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -334,6 +334,20 @@ void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) m_moves_slider->Render(canvas_width, canvas_height, extra_scale); } +float Preview::get_moves_slider_height() +{ + if (m_moves_slider && m_moves_slider->IsShown()) + return m_moves_slider->GetHeight(); + return 0.0f; +} + +float Preview::get_layers_slider_width() +{ + if (m_layers_slider && m_layers_slider->IsShown()) + return m_layers_slider->GetWidth(); + return 0.0f; +} + void Preview::bind_event_handlers() { Bind(wxEVT_SIZE, &Preview::on_size, this); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index b2dd52885c..f901447a43 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -136,6 +136,8 @@ public: void msw_rescale(); void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); + float get_layers_slider_width(); + float get_moves_slider_height(); bool is_loaded() const { return m_loaded; } diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 97f7b00739..0f949ddba4 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -67,7 +67,9 @@ public: m_size = size; m_draw_opts.scale = scale; } - + ImVec2 GetCtrlSize() { return m_size; } + ImVec2 GetCtrlPos() { return m_pos; } + void Show(bool show) { m_is_shown = show; } void Hide() { m_is_shown = false; } bool IsShown() const { return m_is_shown; } @@ -239,6 +241,8 @@ public: void SetEmUnit(int em_unit) { m_em = em_unit; } void ShowLowerThumb(bool show) { m_ctrl.ShowLowerThumb(show); } + float GetWidth() { return m_ctrl.GetCtrlSize().x; } + float GetHeight() { return m_ctrl.GetCtrlSize().y; } virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) = 0; void set_callback_on_thumb_move(std::function cb) { m_cb_thumb_move = cb; }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index ce2f36fab8..2cbc89f577 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -174,6 +174,14 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init m_top_y = initial_y + m_window_height; ImVec2 win_pos(1.0f * (float)cnv_size.get_width() - right_gap, 1.0f * (float)cnv_size.get_height() - m_top_y); + + if (wxGetApp().plater()->is_preview_shown()) { + if (Preview* preview = dynamic_cast(canvas.get_wxglcanvas()->GetParent())) { + win_pos.y -= 1.5f * preview->get_moves_slider_height(); + win_pos.x -= preview->get_layers_slider_width(); + } + } + ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); ImGuiPureWrap::set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); From 0bb9233da73f62a3ff869479f5155fee1254694c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 23 Apr 2024 14:01:16 +0200 Subject: [PATCH 13/13] ImguiDoubleSlider: Respect to "Collapse sidebar" button + use scale from canvas Size --- src/slic3r/GUI/DoubleSliderForGcode.cpp | 2 +- src/slic3r/GUI/DoubleSliderForGcode.hpp | 2 +- src/slic3r/GUI/DoubleSliderForLayers.cpp | 6 +++--- src/slic3r/GUI/DoubleSliderForLayers.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 +------- src/slic3r/GUI/GUI_Preview.cpp | 14 +++++++++----- src/slic3r/GUI/GUI_Preview.hpp | 2 +- src/slic3r/GUI/ImGuiDoubleSlider.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 10 +++++----- src/slic3r/GUI/Plater.hpp | 2 +- 10 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/DoubleSliderForGcode.cpp b/src/slic3r/GUI/DoubleSliderForGcode.cpp index 0924f279ff..7ad6c3bf81 100644 --- a/src/slic3r/GUI/DoubleSliderForGcode.cpp +++ b/src/slic3r/GUI/DoubleSliderForGcode.cpp @@ -10,7 +10,7 @@ namespace DoubleSlider { static const float LEFT_MARGIN = 13.0f + 100.0f; // avoid thumbnail toolbar static const float HORIZONTAL_SLIDER_HEIGHT = 45.0f; -void DSForGcode::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) +void DSForGcode::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/, float offset/* = 0.f*/) { if (!m_ctrl.IsShown()) return; diff --git a/src/slic3r/GUI/DoubleSliderForGcode.hpp b/src/slic3r/GUI/DoubleSliderForGcode.hpp index c216fe3017..0cb528b125 100644 --- a/src/slic3r/GUI/DoubleSliderForGcode.hpp +++ b/src/slic3r/GUI/DoubleSliderForGcode.hpp @@ -22,7 +22,7 @@ public: } ~DSForGcode() {} - void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override; void set_render_as_disabled(bool value) { m_render_as_disabled = value; } bool is_rendering_as_disabled() const { return m_render_as_disabled; } diff --git a/src/slic3r/GUI/DoubleSliderForLayers.cpp b/src/slic3r/GUI/DoubleSliderForLayers.cpp index fdf9cbfa49..3f0a2e1eaf 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.cpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.cpp @@ -615,7 +615,7 @@ bool DSForLayers::render_jump_to_window(const ImVec2& pos, double* active_value, return enter_pressed || ok_pressed; } -void DSForLayers::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/) +void DSForLayers::Render(const int canvas_width, const int canvas_height, float extra_scale/* = 0.1f*/, float offset /*= 0.f*/) { if (!m_ctrl.IsShown()) return; @@ -627,11 +627,11 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float ImVec2 pos; pos.x = canvas_width - VERTICAL_SLIDER_WIDTH * m_scale - tick_icon_side; - pos.y = 1.5f * action_btn_sz; + pos.y = 1.5f * action_btn_sz + offset; if (m_allow_editing) pos.y += 2.f; - ImVec2 size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz); + ImVec2 size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz - offset); m_ctrl.Init(pos, size, m_scale); if (m_ctrl.render()) { diff --git a/src/slic3r/GUI/DoubleSliderForLayers.hpp b/src/slic3r/GUI/DoubleSliderForLayers.hpp index 620c21c530..5eea05913e 100644 --- a/src/slic3r/GUI/DoubleSliderForLayers.hpp +++ b/src/slic3r/GUI/DoubleSliderForLayers.hpp @@ -73,7 +73,7 @@ public: void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder); - void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) override; + void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override; // jump to selected layer void jump_to_value(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7c111d63c5..0348f811d9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1947,13 +1947,7 @@ void GLCanvas3D::render() wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); - wxGetApp().plater()->render_sliders(*this, -#if ENABLE_RETINA_GL - m_retina_helper->get_scale_factor() -#else - 1.f -#endif - ); + wxGetApp().plater()->render_sliders(*this); wxGetApp().imgui()->render(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 4b61a0c65e..bb773bf421 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -322,14 +322,18 @@ void Preview::msw_rescale() reload_print(); } -void Preview::render_sliders(GLCanvas3D& canvas, float extra_scale/* = 0.1f*/) +void Preview::render_sliders(GLCanvas3D& canvas) { - const Size cnv_size = canvas.get_canvas_size(); - const int canvas_width = cnv_size.get_width(); - const int canvas_height = cnv_size.get_height(); + const Size cnv_size = canvas.get_canvas_size(); + const int canvas_width = cnv_size.get_width(); + const int canvas_height = cnv_size.get_height(); + const float extra_scale = cnv_size.get_scale_factor(); + + GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); + const bool is_collapse_btn_shown = collapse_toolbar.is_enabled(); if (m_layers_slider) - m_layers_slider->Render(canvas_width, canvas_height, extra_scale); + m_layers_slider->Render(canvas_width, canvas_height, extra_scale, is_collapse_btn_shown ? collapse_toolbar.get_height() : 0.f); if (m_moves_slider) m_moves_slider->Render(canvas_width, canvas_height, extra_scale); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index f901447a43..9d8f85304d 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -135,7 +135,7 @@ public: void msw_rescale(); - void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); + void render_sliders(GLCanvas3D& canvas); float get_layers_slider_width(); float get_moves_slider_height(); diff --git a/src/slic3r/GUI/ImGuiDoubleSlider.hpp b/src/slic3r/GUI/ImGuiDoubleSlider.hpp index 0f949ddba4..59493e8880 100644 --- a/src/slic3r/GUI/ImGuiDoubleSlider.hpp +++ b/src/slic3r/GUI/ImGuiDoubleSlider.hpp @@ -243,7 +243,7 @@ public: float GetWidth() { return m_ctrl.GetCtrlSize().x; } float GetHeight() { return m_ctrl.GetCtrlSize().y; } - virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f) = 0; + virtual void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) = 0; void set_callback_on_thumb_move(std::function cb) { m_cb_thumb_move = cb; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 73f3d7af20..3ba7588d34 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -385,7 +385,7 @@ struct Plater::priv void set_current_canvas_as_dirty(); GLCanvas3D* get_current_canvas3D(); - void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); + void render_sliders(GLCanvas3D& canvas); void unbind_canvas_event_handlers(); void reset_canvas_volumes(); @@ -3185,10 +3185,10 @@ GLCanvas3D* Plater::priv::get_current_canvas3D() return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); } -void Plater::priv::render_sliders(GLCanvas3D& canvas, float extra_scale /*= 0.1f*/) +void Plater::priv::render_sliders(GLCanvas3D& canvas) { if (current_panel == preview) - preview->render_sliders(canvas, extra_scale); + preview->render_sliders(canvas); } void Plater::priv::unbind_canvas_event_handlers() @@ -6404,9 +6404,9 @@ GLCanvas3D* Plater::get_current_canvas3D() return p->get_current_canvas3D(); } -void Plater::render_sliders(GLCanvas3D& canvas, float extra_scale) +void Plater::render_sliders(GLCanvas3D& canvas) { - p->render_sliders(canvas, extra_scale); + p->render_sliders(canvas); } static std::string concat_strings(const std::set &strings, diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 656ef4a2f7..462c875e2e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -272,7 +272,7 @@ public: const GLCanvas3D * canvas3D() const; GLCanvas3D* get_current_canvas3D(); - void render_sliders(GLCanvas3D& canvas, float extra_scale = 0.1f); + void render_sliders(GLCanvas3D& canvas); void arrange(); void arrange(Worker &w, bool selected);