ImguiDoubleSlider: WIP: Implemented "pure" ImGuiControl

+ For ImGuiControl implemented adding of callbacks for get_label and draw_scroll_line
This commit is contained in:
YuSanka 2024-03-14 02:01:52 +01:00 committed by Lukas Matena
parent a26d17d689
commit ab9923eee6
8 changed files with 754 additions and 496 deletions

View File

@ -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

View File

@ -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<double>& values)
@ -294,6 +299,8 @@ void Control::SetSliderValues(const std::vector<double>& 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;
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::NewLine();
if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, "one_layer", fiOneLayerIcon))
switch_one_layer_mode();
imgui_ctrl.SetPos(pos);
imgui_ctrl.SetSize(size);
imgui_ctrl.SetScale(scale);
ImGui::NewLine();
if (m_draw_mode != dmSequentialGCodeView && render_button(ImGui::PrintIconMarker, "settings", fiCogIcon))
show_cog_icon_context_menu();
ImGuiPureWrap::end();
}
ImGui::PopStyleVar(3);
ImGui::PopStyleColor(2);
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));
@ -3065,6 +2723,8 @@ void Control::add_code_as_tick(Type type, int selected_extruder/* = -1*/)
const int extruder = selected_extruder > 0 ? selected_extruder : std::max<int>(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
if (!m_ticks.add_tick(tick, type, extruder, m_values[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,6 +2843,7 @@ 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();
}

View File

@ -9,6 +9,8 @@
#include "wxExtensions.hpp"
#include "GLCanvas3D.hpp"
#include "ImGuiDoubleSlider.hpp"
#include <wx/window.h>
#include <wx/control.h>
#include <wx/dc.h>
@ -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;
};

View File

@ -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"));

View File

@ -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<double>& 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<unsigned long>(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

View File

@ -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 <sstream>
// this code is borrowed from https://stackoverflow.com/questions/16605967/set-precision-of-stdto-string-when-converting-floating-point-values
template <typename T>
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<double>& 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<std::string(int)> cb) { m_cb_get_label_on_move = cb; }
void set_get_label_cb(std::function<std::string(int)> cb) { m_cb_get_label = cb; }
void set_draw_scroll_line_cb(std::function<void(const ImRect&, const ImRect&)> 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<std::string(int)> m_cb_get_label { nullptr };
std::function<std::string(int)> m_cb_get_label_on_move { nullptr };
std::function<void(const ImRect&, const ImRect&)> 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<double> m_values;
DrawOptions m_draw_opts;
Regions m_regions;
};
} // GUI
} // Slic3r
#endif // slic3r_ImGUI_DoubleSlider_hpp_

View File

@ -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();

View File

@ -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);