Preview sliders overhaul (#13919)

* Make preview slider labels draggable

Add label hit testing and delta-based dragging for the vertical preview slider labels. Keep label drags tied to the selected handle, prevent slider hover/timeline/menu handling from stealing label interactions, and keep value setters from changing the active selection implicitly.

* Refresh preview slider visuals

Update preview slider rails, handles, and labels for the refreshed light and dark theme appearance. Apply the same visual language to the horizontal slider, align single-layer and multi-layer labels, and remove obsolete triangle label geometry.
This commit is contained in:
Kiss Lorand
2026-05-31 13:55:14 +03:00
committed by GitHub
parent c714935596
commit 3a8dfeaa08

View File

@@ -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<float>(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<int, 2> IMSlider::get_active_extruders_for_tick(int tick) const
}
} // Slic3r