From ab9923eee640b92e8c315ccb4d7b899f4c30740d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 14 Mar 2024 02:01:52 +0100 Subject: [PATCH] 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);