diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index d5945a2c1a..8a999d85f5 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -2,6 +2,7 @@ #include "libslic3r/GCode.hpp" #include "GUI_App.hpp" #include "NotificationManager.hpp" +#include "Widgets/StateColor.hpp" #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif @@ -30,7 +31,6 @@ static const ImU32 GROOVE_COLOR_DARK = IM_COL32(45, 45, 49, 255); static const ImU32 GROOVE_COLOR_LIGHT = IM_COL32(206, 206, 206, 255); static const ImU32 BRAND_COLOR = IM_COL32(0, 150, 136, 255); - static int m_tick_value = -1; static ImVec4 m_tick_rect; @@ -178,7 +178,6 @@ int IMSlider::GetActiveValue() const void IMSlider::SetLowerValue(const int lower_val) { - m_selection = ssLower; m_lower_value = lower_val; correct_lower_value(); set_as_dirty(); @@ -186,7 +185,6 @@ void IMSlider::SetLowerValue(const int lower_val) void IMSlider::SetHigherValue(const int higher_val) { - m_selection = ssHigher; m_higher_value = higher_val; correct_higher_value(); set_as_dirty(); @@ -455,7 +453,7 @@ bool IMSlider::switch_one_layer_mode() m_is_one_layer = !m_is_one_layer; if (!m_is_one_layer) { // DEACTIVATE - m_one_layer_value = GetHigherValue(); // ORCA Backup value on deactivate + m_one_layer_value = GetHigherValue(); // ORCA Backup value on deactivate SetLowerValue(m_min_value); SetHigherValue(m_max_value); // Higher value resets on toggling off one layer mode to show whole model }else{ // ACTIVATE @@ -465,11 +463,10 @@ bool IMSlider::switch_one_layer_mode() SetHigherValue(m_one_layer_value); } else if(GetHigherValue() == m_max_value) // ORCA Prefer backup value if higher value reseted - SetHigherValue(m_one_layer_value); // ORCA Restore value + SetHigherValue(m_one_layer_value); // ORCA Restore value else // ORCA Prefer higher value if user changed higher value. so it will show section on same view SetHigherValue(GetHigherValue()); // ORCA use same position with higher value if user changed its position. visible section stays same when switching one layer mode with this } - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (m_selection == ssUndef) m_selection = ssHigher; set_as_dirty(); return true; @@ -503,15 +500,23 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int const float handle_radius = 12.0f * m_scale; const float handle_border = 2.0f * m_scale; - - const float text_frame_rounding = 2.0f * scale * m_scale; const float text_start_offset = 8.0f * m_scale; const ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; - const float triangle_offsets[3] = {-3.5f * m_scale, 3.5f * m_scale, -6.06f * m_scale}; - const ImU32 white_bg = m_is_dark ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT; const ImU32 handle_clr = BRAND_COLOR; const ImU32 handle_border_clr = m_is_dark ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT; + const wxColour label_bg = StateColor::darkModeColorFor(wxGetApp().get_window_default_clr()); + const wxColour label_border = StateColor::darkModeColorFor(wxColour("#CECECE")); + const wxColour rail_inner_bg = m_is_dark ? StateColor::darkModeColorFor(wxColour("#CECECE")) : wxGetApp().get_highlight_default_clr(); + const wxColour rail_border = m_is_dark ? StateColor::darkModeColorFor(wxColour("#F0F0F1")) : wxColour("#CECECE"); + const ImU32 label_bg_clr = IM_COL32(label_bg.Red(), label_bg.Green(), label_bg.Blue(), 238); + const ImU32 label_border_clr = IM_COL32(label_border.Red(), label_border.Green(), label_border.Blue(), 255); + const ImU32 label_shadow_clr = m_is_dark ? IM_COL32(0, 0, 0, 84) : IM_COL32(0, 0, 0, 38); + ImVec4 range_fill = ImGui::ColorConvertU32ToFloat4(BRAND_COLOR); + range_fill.w = (m_is_dark ? 210.0f : 190.0f) / 255.0f; + const ImU32 range_fill_clr = ImGui::GetColorU32(range_fill); + const ImU32 rail_inner_clr = IM_COL32(rail_inner_bg.Red(), rail_inner_bg.Green(), rail_inner_bg.Blue(), 255); + const ImU32 rail_border_clr = IM_COL32(rail_border.Red(), rail_border.Green(), rail_border.Blue(), 190); // calculate groove size const ImVec2 groove_start = ImVec2(pos.x + handle_dummy_width, pos.y + size.y - ONE_LAYER_MARGIN.y * m_scale - (ONE_LAYER_BUTTON_SIZE.y / 2) * m_scale * 0.5f - GROOVE_WIDTH * m_scale * 0.5f); @@ -521,8 +526,8 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int const float mid_y = groove.GetCenter().y; // set mouse active region. active region. - bool hovered = ImGui::ItemHoverable(draw_region, id); - if (hovered && context.IO.MouseDown[0]) { + bool slider_hovered = ImGui::ItemHoverable(draw_region, id); + if (slider_hovered && context.IO.MouseDown[0]) { ImGui::SetActiveID(id, window); ImGui::SetFocusID(id, window); ImGui::FocusWindow(window); @@ -530,6 +535,9 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int // draw background draw_background_and_groove(bg_rect, groove); + window->DrawList->AddRect(groove.Min, groove.Max, rail_border_clr, 0.5f * groove.GetHeight(), 0, 1.0f * m_scale); + const ImRect rail_inner(groove.Min + ImVec2(2.0f, 2.0f) * m_scale, groove.Max - ImVec2(2.0f, 2.0f) * m_scale); + window->DrawList->AddRectFilled(rail_inner.Min, rail_inner.Max, rail_inner_clr, 0.5f * rail_inner.GetHeight()); // set scrollable region const ImRect slideable_region = ImRect(bg_rect.Min + ImVec2(handle_radius, 0.0f), bg_rect.Max - ImVec2(handle_radius, 0.0f)); @@ -543,25 +551,29 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int ImVec2 handle_center = handle.GetCenter(); // draw scroll line - ImRect scroll_line = ImRect(groove.Min, ImVec2(handle_center.x, groove.Max.y)); - window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr, 0.5f * GROOVE_WIDTH * m_scale); + ImRect scroll_line = ImRect(ImVec2(groove.Min.x, groove.Min.y - 2.0f * m_scale), + ImVec2(handle_center.x, groove.Max.y + 2.0f * m_scale)); + window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, range_fill_clr, 0.5f * scroll_line.GetHeight()); // draw handle + window->DrawList->AddCircleFilled(handle_center, handle_radius + 2.0f * m_scale, handle_border_clr); window->DrawList->AddCircleFilled(handle_center, handle_radius, handle_border_clr); window->DrawList->AddCircleFilled(handle_center, handle_radius - handle_border, handle_clr); + window->DrawList->AddCircle(handle_center, handle_radius + 3.0f * m_scale, handle_clr, 0, 2.0f * m_scale); // draw label - auto text_utf8 = into_u8(std::to_string(*value)); - ImVec2 text_content_size = ImGui::CalcTextSize(text_utf8.c_str()); + const std::string value_label = std::to_string(*value); + const ImVec2 text_content_size = ImGui::CalcTextSize(value_label.c_str()); ImVec2 text_size = text_content_size + text_padding * 2; ImVec2 text_start = ImVec2(handle_center.x + handle_radius + text_start_offset, handle_center.y - 0.5 * text_size.y); ImRect text_rect(text_start, text_start + text_size); - ImGui::RenderFrame(text_rect.Min, text_rect.Max, white_bg, false, text_frame_rounding); - ImVec2 pos_1 = ImVec2(text_rect.Min.x, text_rect.GetCenter().y + triangle_offsets[0]); - ImVec2 pos_2 = ImVec2(text_rect.Min.x, text_rect.GetCenter().y + triangle_offsets[1]); - ImVec2 pos_3 = ImVec2(text_rect.Min.x + triangle_offsets[2], text_rect.GetCenter().y); - window->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, white_bg); - ImGui::RenderText(text_start + text_padding, std::to_string(*value).c_str()); + const float label_rounding = 5.0f * m_scale; + const ImVec2 shadow_offset = ImVec2(2.0f, 2.0f) * m_scale; + window->DrawList->AddRectFilled(text_rect.Min + shadow_offset, text_rect.Max + shadow_offset, label_shadow_clr, label_rounding); + ImGui::RenderFrame(text_rect.Min, text_rect.Max, label_bg_clr, false, label_rounding); + window->DrawList->AddRect(text_rect.Min, text_rect.Max, label_border_clr, label_rounding, 0, 1.0f * m_scale); + ImGui::RenderText(text_rect.Min + ImVec2((text_size.x - text_content_size.x) * 0.5f, + (text_size.y - text_content_size.y) * 0.5f), value_label.c_str()); return value_changed; } @@ -881,18 +893,27 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower const float handle_border = 2.0f * m_scale; const float line_width = 1.0f * m_scale; const float line_length = 12.0f * m_scale; - const float one_handle_offset = 26.0f * m_scale; - const float bar_width = 28.0f * m_scale; - const float text_frame_rounding = 2.0f * scale * m_scale; const ImVec2 text_padding = ImVec2(5.0f, 2.0f) * m_scale; - const 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; ImVec2 text_size; const ImU32 white_bg = m_is_dark ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT; const ImU32 handle_clr = BRAND_COLOR; const ImU32 handle_border_clr = m_is_dark ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT; + const wxColour label_bg = StateColor::darkModeColorFor(wxGetApp().get_window_default_clr()); + const wxColour label_bg_active = StateColor::darkModeColorFor(wxColour("#E5F0EE")); + const wxColour label_border = StateColor::darkModeColorFor(wxColour("#CECECE")); + const wxColour rail_inner_bg = m_is_dark ? StateColor::darkModeColorFor(wxColour("#CECECE")) : wxGetApp().get_highlight_default_clr(); + const wxColour rail_border = m_is_dark ? StateColor::darkModeColorFor(wxColour("#F0F0F1")) : wxColour("#CECECE"); + const ImU32 label_bg_clr = IM_COL32(label_bg.Red(), label_bg.Green(), label_bg.Blue(), 238); + const ImU32 label_bg_active_clr = IM_COL32(label_bg_active.Red(), label_bg_active.Green(), label_bg_active.Blue(), 246); + const ImU32 label_border_clr = IM_COL32(label_border.Red(), label_border.Green(), label_border.Blue(), 255); + const ImU32 label_shadow_clr = m_is_dark ? IM_COL32(0, 0, 0, 84) : IM_COL32(0, 0, 0, 38); + ImVec4 range_fill = ImGui::ColorConvertU32ToFloat4(BRAND_COLOR); + range_fill.w = (m_is_dark ? 210.0f : 190.0f) / 255.0f; + const ImU32 range_fill_clr = ImGui::GetColorU32(range_fill); + const ImU32 rail_inner_clr = IM_COL32(rail_inner_bg.Red(), rail_inner_bg.Green(), rail_inner_bg.Blue(), 255); + const ImU32 rail_border_clr = IM_COL32(rail_border.Red(), rail_border.Green(), rail_border.Blue(), 190); // calculate slider groove size const ImVec2 groove_start = ImVec2(pos.x + size.x - ONE_LAYER_MARGIN.x * m_scale - (ONE_LAYER_BUTTON_SIZE.x / 2) * m_scale * 0.5f - GROOVE_WIDTH * m_scale * 0.5f, pos.y + text_dummy_height); const ImVec2 groove_size = ImVec2(GROOVE_WIDTH * m_scale, size.y - 2 * text_dummy_height); @@ -900,22 +921,9 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower const ImRect bg_rect = ImRect(groove.Min - ImVec2(6.0f, 6.0f) * m_scale, groove.Max + ImVec2(6.0f, 6.0f) * m_scale); const float mid_x = groove.GetCenter().x; // ORCA: tune label box width to fit the slider window without overlapping the groove. - const float label_extra_padding = 10.0f * m_scale; - const float one_layer_extra_padding = 6.0f * m_scale; + const float label_width_margin = 10.0f * m_scale; const float max_label_width = std::max(0.0f, - groove.Min.x - draw_region.Min.x - triangle_offsets[2].x - text_padding.x * 2.0f - label_extra_padding); - - // set mouse active region. - const ImRect active_region = ImRect(ImVec2(draw_region.Min.x + 35.0f * m_scale, draw_region.Min.y), draw_region.Max); - bool hovered = ImGui::ItemHoverable(active_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); - } - - // draw background - draw_background_and_groove(bg_rect, groove); + groove.Min.x - draw_region.Min.x - label_width_margin * 2.0f - text_padding.x * 2.0f); // Processing interacting // set scrollable region @@ -931,34 +939,162 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower 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)); + auto one_layer_handle = [&](int value) { + const float handle_pos = get_pos_from_value(v_min, v_max, value, one_slideable_region); + return ImRect(mid_x - handle_radius, handle_pos - handle_radius, + mid_x + handle_radius, handle_pos + handle_radius); + }; + ImRect one_handle; + if (one_layer_flag) + one_handle = one_layer_handle(*higher_value); + + // Label hit testing enables delta-based label drag without jumping to the mouse position. + SelectedSlider hovered_label = ssUndef; + const bool menu_open = ImGui::IsPopupOpen("slider_add_menu_popup") || ImGui::IsPopupOpen("slider_edit_menu_popup"); + const ImVec2 higher_text_content_size = ImGui::CalcTextSize(into_u8(higher_label).c_str()); + const ImVec2 lower_text_content_size = one_layer_flag ? ImVec2() : ImGui::CalcTextSize(into_u8(lower_label).c_str()); + auto label_hit = [&](const ImRect& label_rect, SelectedSlider selection_value) { + if (!label_rect.Contains(context.IO.MousePos)) + return; + hovered_label = selection_value; + }; + auto range_label_rect = [&](const ImRect& handle, const ImVec2& content_size, bool top_label) { + const ImVec2 text_size = ImVec2(max_label_width, content_size.y) + text_padding * 2.0f; + const ImVec2 text_start = ImVec2(handle.Min.x - text_size.x - label_width_margin, + top_label ? handle.GetCenter().y - text_size.y : handle.GetCenter().y); + return ImRect(text_start, text_start + text_size + ImVec2(label_width_margin, 0.0f)); + }; + auto one_layer_label_rect = [&](const ImRect& handle) { + const ImVec2 text_size = ImVec2(max_label_width, higher_text_content_size.y) + text_padding * 2.0f; + const ImVec2 text_start = ImVec2(handle.Min.x - text_size.x - label_width_margin, + handle.GetCenter().y - 0.5f * text_size.y); + return ImRect(text_start, text_start + text_size); + }; + auto draw_label = [&](const ImRect& rect, const ImVec2& content_size, const std::string& label, bool hovered, bool active) { + const float rounding = 5.0f * m_scale; + const ImU32 bg_clr = active ? label_bg_active_clr : label_bg_clr; + const ImVec2 shadow_offset = ImVec2(2.0f, 2.0f) * m_scale; + window->DrawList->AddRectFilled(rect.Min + shadow_offset, rect.Max + shadow_offset, label_shadow_clr, rounding); + ImGui::RenderFrame(rect.Min, rect.Max, bg_clr, false, rounding); + window->DrawList->AddRect(rect.Min, rect.Max, hovered ? handle_clr : label_border_clr, rounding, 0, hovered ? 1.5f * m_scale : 1.0f * m_scale); + const ImVec2 rect_size = rect.GetSize(); + ImGui::RenderText(rect.Min + ImVec2((rect_size.x - content_size.x) * 0.5f, + (rect_size.y - content_size.y) * 0.5f), label.c_str()); + }; + auto draw_handle = [&](const ImVec2& center) { + window->DrawList->AddCircleFilled(center, handle_radius, handle_border_clr); + window->DrawList->AddCircleFilled(center, handle_radius - handle_border, handle_clr); + }; + auto draw_active_handle = [&](const ImVec2& center) { + window->DrawList->AddCircleFilled(center, handle_radius + 2.0f * m_scale, handle_border_clr); + draw_handle(center); + window->DrawList->AddCircle(center, handle_radius + 3.0f * m_scale, handle_clr, 0, 2.0f * m_scale); + window->DrawList->AddLine(center + ImVec2(-0.5f * line_length, 0.0f), center + ImVec2(0.5f * line_length, 0.0f), white_bg, line_width); + window->DrawList->AddLine(center + ImVec2(0.0f, -0.5f * line_length), center + ImVec2(0.0f, 0.5f * line_length), white_bg, line_width); + }; + + // Prevent interaction with labels if slider add/edit menu is open + // or the mouse was pressed elsewhere and then dragged over them. + if (!menu_open && (!context.IO.MouseDown[0] || context.IO.MouseClicked[0])) { + if (!one_layer_flag) { + label_hit(range_label_rect(higher_handle, higher_text_content_size, true), ssHigher); + label_hit(range_label_rect(lower_handle, lower_text_content_size, false), ssLower); + } else { + label_hit(one_layer_label_rect(one_handle), ssHigher); + } + } + + // set mouse active region + const ImRect slider_active_region = ImRect(ImVec2(draw_region.Min.x + 35.0f * m_scale, draw_region.Min.y), draw_region.Max); + bool slider_hovered = !menu_open && ImGui::ItemHoverable(slider_active_region, id) && !ImGui::ItemHoverable(m_tick_rect, id) && hovered_label == ssUndef; + struct LabelDragState + { + ImGuiID id = 0; + SelectedSlider selection = ssUndef; + ImVec2 start_mouse; + int start_value = 0; + }; + // Persist the label that started the drag after the cursor leaves its rect. + static LabelDragState label_drag; + + if (hovered_label != ssUndef && context.IO.MouseClicked[0]) { + selection = hovered_label; + label_drag.id = id; + label_drag.selection = hovered_label; + label_drag.start_mouse = context.IO.MousePos; + label_drag.start_value = hovered_label == ssHigher ? *higher_value : *lower_value; + ImGui::SetActiveID(id, window); + ImGui::SetFocusID(id, window); + ImGui::FocusWindow(window); + } + if (slider_hovered && context.IO.MouseDown[0]) { + ImGui::SetActiveID(id, window); + ImGui::SetFocusID(id, window); + ImGui::FocusWindow(window); + } + + // draw background + draw_background_and_groove(bg_rect, groove); + window->DrawList->AddRect(groove.Min, groove.Max, rail_border_clr, 0.5f * groove.GetWidth(), 0, 1.0f * m_scale); + const ImRect rail_inner(groove.Min + ImVec2(2.0f, 2.0f) * m_scale, groove.Max - ImVec2(2.0f, 2.0f) * m_scale); + window->DrawList->AddRectFilled(rail_inner.Min, rail_inner.Max, rail_inner_clr, 0.5f * rail_inner.GetWidth()); bool value_changed = false; if (!one_layer_flag) { - // select higher handle by default - static bool h_selected = (selection == ssHigher); - 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; + const SelectedSlider dragged_label = label_drag.id == id && context.IO.MouseDown[0] ? label_drag.selection : ssUndef; + if (dragged_label == ssUndef && !menu_open) { + if (ImGui::ItemHoverable(higher_handle, id) && context.IO.MouseClicked[0]) { + selection = ssHigher; + } + if (ImGui::ItemHoverable(lower_handle, id) && context.IO.MouseClicked[0]) { + selection = ssLower; + } } + bool h_selected = selection != ssLower; // 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); + if (dragged_label != ssUndef) { + const ImRect& drag_region = dragged_label == ssHigher ? higher_slideable_region : lower_slideable_region; + const float region_height = drag_region.GetHeight(); + if (region_height > 0.0f) { + const float delta = context.IO.MousePos.y - label_drag.start_mouse.y; + const float value_delta = delta * (float)(v_max - v_min) / region_height; + const int new_value = (int)ImClamp((float)label_drag.start_value - value_delta, (float)v_min, (float)v_max); + if (dragged_label == ssHigher) { + value_changed = *higher_value != new_value; + *higher_value = new_value; + } else { + value_changed = *lower_value != new_value; + *lower_value = new_value; + } + } + h_selected = dragged_label == ssHigher; + if (dragged_label == ssHigher) { + higher_handle_pos = get_pos_from_value(v_min, v_max, *higher_value, higher_slideable_region); + higher_handle = ImRect(mid_x - handle_radius, higher_handle_pos - handle_radius, mid_x + handle_radius, higher_handle_pos + handle_radius); + } else { + lower_handle_pos = get_pos_from_value(v_min, v_max, *lower_value, lower_slideable_region); + lower_handle = ImRect(mid_x - handle_radius, lower_handle_pos - handle_radius, mid_x + handle_radius, lower_handle_pos + handle_radius); + } + } else { + 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); + } } + SelectedSlider active_label = ssUndef; + if (dragged_label != ssUndef) + active_label = dragged_label; + else if (context.ActiveId == id && context.IO.MouseDown[0]) + active_label = h_selected ? ssHigher : ssLower; ImVec2 higher_handle_center = higher_handle.GetCenter(); ImVec2 lower_handle_center = lower_handle.GetCenter(); @@ -978,10 +1114,10 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower } // judge whether to open menu - if (ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) + if (!menu_open && 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]) + if (!menu_open && ((!ImGui::ItemHoverable(h_selected ? higher_handle : lower_handle, id) && context.IO.MouseClicked[1]) || + context.IO.MouseClicked[0])) m_show_menu = false; // draw ticks @@ -991,107 +1127,94 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower if (!m_ticks.has_tick_with_code(ToolChange)) { // draw scroll line - ImRect scroll_line = ImRect(ImVec2(groove.Min.x, higher_handle_center.y), ImVec2(groove.Max.x, lower_handle_center.y)); - window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, handle_clr); + ImRect scroll_line = ImRect(ImVec2(groove.Min.x - 2.0f * m_scale, higher_handle_center.y), + ImVec2(groove.Max.x + 2.0f * m_scale, lower_handle_center.y)); + window->DrawList->AddRectFilled(scroll_line.Min, scroll_line.Max, range_fill_clr, 0.5f * scroll_line.GetWidth()); } // draw handles - window->DrawList->AddCircleFilled(higher_handle_center, handle_radius, handle_border_clr); - window->DrawList->AddCircleFilled(higher_handle_center, handle_radius - handle_border, handle_clr); - window->DrawList->AddCircleFilled(lower_handle_center, handle_radius, handle_border_clr); - window->DrawList->AddCircleFilled(lower_handle_center, handle_radius - handle_border, handle_clr); - if (h_selected) { - window->DrawList->AddCircleFilled(higher_handle_center, handle_radius, handle_border_clr); - window->DrawList->AddCircleFilled(higher_handle_center, handle_radius - handle_border, handle_clr); - window->DrawList->AddLine(higher_handle_center + ImVec2(-0.5f * line_length, 0.0f), higher_handle_center + ImVec2(0.5f * line_length, 0.0f), white_bg, line_width); - window->DrawList->AddLine(higher_handle_center + ImVec2(0.0f, -0.5f * line_length), higher_handle_center + ImVec2(0.0f, 0.5f * line_length), white_bg, line_width); - } - if (!h_selected) { - window->DrawList->AddLine(lower_handle_center + ImVec2(-0.5f * line_length, 0.0f), lower_handle_center + ImVec2(0.5f * line_length, 0.0f), white_bg, line_width); - window->DrawList->AddLine(lower_handle_center + ImVec2(0.0f, -0.5f * line_length), lower_handle_center + ImVec2(0.0f, 0.5f * line_length), white_bg, line_width); - } + draw_handle(higher_handle_center); + draw_handle(lower_handle_center); + draw_active_handle(h_selected ? higher_handle_center : lower_handle_center); - // ORCA: render fixed-width label boxes - // draw higher label - auto text_utf8 = into_u8(higher_label); - text_content_size = ImGui::CalcTextSize(text_utf8.c_str()); - text_size = ImVec2(max_label_width, text_content_size.y) + text_padding * 2; - ImVec2 text_start = ImVec2(higher_handle.Min.x - text_size.x - triangle_offsets[2].x, higher_handle_center.y - text_size.y); - ImRect text_rect(text_start, text_start + text_size); - ImGui::RenderFrame(text_rect.Min, text_rect.Max, white_bg, false, text_frame_rounding); - ImVec2 pos_1 = text_rect.Max - triangle_offsets[0]; - ImVec2 pos_2 = pos_1 - triangle_offsets[1]; - ImVec2 pos_3 = pos_1 + triangle_offsets[2]; - window->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, white_bg); - ImGui::RenderText(text_start + ImVec2((text_size.x - text_content_size.x) * 0.5f, - (text_size.y - text_content_size.y) * 0.5f), higher_label.c_str()); - // draw lower label - text_utf8 = into_u8(lower_label); - text_content_size = ImGui::CalcTextSize(text_utf8.c_str()); - text_size = ImVec2(max_label_width, text_content_size.y) + text_padding * 2; - text_start = ImVec2(lower_handle.Min.x - text_size.x - triangle_offsets[2].x, lower_handle_center.y); - text_rect = ImRect(text_start, text_start + text_size); - ImGui::RenderFrame(text_rect.Min, text_rect.Max, white_bg, false, text_frame_rounding); - pos_1 = ImVec2(text_rect.Max.x, text_rect.Min.y) - triangle_offsets[0]; - pos_2 = pos_1 + triangle_offsets[1]; - pos_3 = pos_1 + triangle_offsets[2]; - window->DrawList->AddTriangleFilled(pos_1, pos_2, pos_3, white_bg); - ImGui::RenderText(text_start + ImVec2((text_size.x - text_content_size.x) * 0.5f, - (text_size.y - text_content_size.y) * 0.5f), lower_label.c_str()); + // ORCA: render fixed-width label boxes + // draw higher label + text_size = ImVec2(max_label_width, higher_text_content_size.y) + text_padding * 2; + ImVec2 text_start = ImVec2(higher_handle.Min.x - text_size.x - label_width_margin, higher_handle_center.y - text_size.y); + ImRect text_rect(text_start, text_start + text_size); + const bool higher_label_active = active_label == ssHigher; + draw_label(text_rect, higher_text_content_size, higher_label, + hovered_label == ssHigher || higher_label_active, higher_label_active); + // draw lower label + text_size = ImVec2(max_label_width, lower_text_content_size.y) + text_padding * 2; + text_start = ImVec2(lower_handle.Min.x - text_size.x - label_width_margin, lower_handle_center.y); + text_rect = ImRect(text_start, text_start + text_size); + const bool lower_label_active = active_label == ssLower; + draw_label(text_rect, lower_text_content_size, lower_label, + hovered_label == ssLower || lower_label_active, lower_label_active); // draw mouse position - if (hovered) { + if (slider_hovered && !context.IO.MouseDown[0]) { draw_tick_on_mouse_position(h_selected ? higher_slideable_region : lower_slideable_region); } } if (one_layer_flag) { // update handle position - value_changed = slider_behavior(id, one_slideable_region, v_min, v_max, - higher_value, &one_handle, ImGuiSliderFlags_Vertical, - m_tick_value, m_tick_rect); + const SelectedSlider dragged_label = label_drag.id == id && context.IO.MouseDown[0] ? label_drag.selection : ssUndef; + if (dragged_label == ssHigher) { + const float region_height = one_slideable_region.GetHeight(); + if (region_height > 0.0f) { + const float delta = context.IO.MousePos.y - label_drag.start_mouse.y; + const float value_delta = delta * (float)(v_max - v_min) / region_height; + const int new_value = (int)ImClamp((float)label_drag.start_value - value_delta, (float)v_min, (float)v_max); + value_changed = *higher_value != new_value; + *higher_value = new_value; + } + one_handle = one_layer_handle(*higher_value); + } else { + value_changed = 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]) + if (!menu_open && 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]) + if (!menu_open && ((!ImGui::ItemHoverable(one_handle, id) && context.IO.MouseClicked[1]) || + context.IO.MouseClicked[0])) m_show_menu = false; - ImVec2 bar_center = higher_handle.GetCenter(); - // 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 - 0.5 * bar_width, handle_center.y), ImVec2(mid_x + 0.5 * bar_width, handle_center.y), handle_clr, 2 * line_width); - window->DrawList->AddCircleFilled(handle_center, handle_radius, handle_border_clr); - window->DrawList->AddCircleFilled(handle_center, handle_radius - handle_border, handle_clr); - window->DrawList->AddLine(handle_center + ImVec2(-0.5f * line_length, 0.0f), handle_center + ImVec2(0.5f * line_length, 0.0f), white_bg, line_width); - window->DrawList->AddLine(handle_center + ImVec2(0.0f, -0.5f * line_length), handle_center + ImVec2(0.0f, 0.5f * line_length), white_bg, line_width); + draw_active_handle(handle_center); // draw label - auto text_utf8 = into_u8(higher_label); - text_content_size = ImGui::CalcTextSize(text_utf8.c_str()); - // ORCA: slightly narrower label box in one-layer mode to avoid left shift. - text_size = ImVec2(std::max(0.0f, max_label_width - label_extra_padding - one_layer_extra_padding), - text_content_size.y) + text_padding * 2; - ImVec2 text_start = ImVec2(one_handle.Min.x - text_size.x, handle_center.y - 0.5 * text_size.y); + text_size = ImVec2(max_label_width, higher_text_content_size.y) + text_padding * 2; + ImVec2 text_start = ImVec2(one_handle.Min.x - text_size.x - label_width_margin, handle_center.y - 0.5 * text_size.y); ImRect text_rect = ImRect(text_start, text_start + text_size); - ImGui::RenderFrame(text_rect.Min, text_rect.Max, white_bg, false, text_frame_rounding); - ImGui::RenderText(text_start + ImVec2((text_size.x - text_content_size.x) * 0.5f, - (text_size.y - text_content_size.y) * 0.5f), higher_label.c_str()); + const bool label_active = context.ActiveId == id && context.IO.MouseDown[0]; + draw_label(text_rect, higher_text_content_size, higher_label, hovered_label == ssHigher || label_active, label_active); // draw mouse position - if (hovered) { + if (slider_hovered && !context.IO.MouseDown[0]) { draw_tick_on_mouse_position(one_slideable_region); } } + if (!context.IO.MouseDown[0] && label_drag.id == id) { + label_drag.id = 0; + label_drag.selection = ssUndef; + if (context.ActiveId == id) + ImGui::ClearActiveID(); + } + return value_changed; } @@ -1130,8 +1253,6 @@ bool IMSlider::render(int canvas_width, int canvas_height) imgui.set_next_window_pos(canvas_width, 0.5f * static_cast(canvas_height), ImGuiCond_Always, 1.0f, 0.5f); imgui.begin(std::string("laysers_slider"), windows_flag); - render_menu(); - int higher_value = GetHigherValue(); int lower_value = GetLowerValue(); std::string higher_label = get_label(m_higher_value); @@ -1146,6 +1267,7 @@ bool IMSlider::render(int canvas_width, int canvas_height) SetLowerValue(lower_value); result = true; } + render_menu(); imgui.end(); imgui.set_next_window_pos(canvas_width, canvas_height, ImGuiCond_Always, 1.0f, 1.0f); @@ -1362,8 +1484,10 @@ void IMSlider::render_add_menu() { int extruder_num = m_extruder_colors.size(); - if (m_show_menu) + if (m_show_menu) { ImGui::OpenPopup("slider_add_menu_popup"); + m_show_menu = false; + } if (ImGui::BeginPopup("slider_add_menu_popup")) { bool menu_item_enable = m_draw_mode != dmSequentialFffPrint; bool hovered = false; @@ -1415,8 +1539,10 @@ void IMSlider::render_add_menu() void IMSlider::render_edit_menu(const TickCode& tick) { - if (m_show_menu) + if (m_show_menu) { ImGui::OpenPopup("slider_edit_menu_popup"); + m_show_menu = false; + } if (ImGui::BeginPopup("slider_edit_menu_popup")) { switch (tick.type) { @@ -1700,5 +1826,3 @@ std::array IMSlider::get_active_extruders_for_tick(int tick) const } } // Slic3r - -