DoubleSliderForLayers Improvements:

* Set "Show estimated print time on mouse moving" on true by default
* Back ported Ruler
* Fix for "Add" button
* Show move line on ruler
This commit is contained in:
YuSanka 2024-06-17 13:51:07 +02:00
parent 102ff4a40e
commit 97d82a0d57
10 changed files with 487 additions and 54 deletions

View File

@ -218,6 +218,15 @@ void AppConfig::set_defaults()
if (get("auth_login_dialog_confirmed").empty())
set("auth_login_dialog_confirmed", "0");
if (get("show_estimated_times_in_dbl_slider").empty())
set("show_estimated_times_in_dbl_slider", "1");
if (get("show_ruler_in_dbl_slider").empty())
set("show_ruler_in_dbl_slider", "0");
if (get("show_ruler_bg_in_dbl_slider").empty())
set("show_ruler_bg_in_dbl_slider", "1");
#ifdef _WIN32
if (get("use_legacy_3DConnexion").empty())
set("use_legacy_3DConnexion", "0");

View File

@ -262,6 +262,8 @@ set(SLIC3R_GUI_SOURCES
GUI/DoubleSliderForLayers.hpp
GUI/DoubleSliderForGcode.cpp
GUI/DoubleSliderForGcode.hpp
GUI/RulerForDoubleSlider.cpp
GUI/RulerForDoubleSlider.hpp
GUI/Notebook.cpp
GUI/Notebook.hpp
GUI/TopBar.cpp

View File

@ -27,7 +27,7 @@ using Slic3r::format;
namespace DoubleSlider {
static const float VERTICAL_SLIDER_WIDTH = 105.0f;
//static const float VERTICAL_SLIDER_WIDTH = 105.0f;
DSForLayers::DSForLayers( int lowerValue,
int higherValue,
@ -42,7 +42,10 @@ DSForLayers::DSForLayers( int lowerValue,
Init(lowerValue, higherValue, minValue, maxValue, "layers_slider", false);
m_ctrl.ShowLabelOnMouseMove(true);
m_ctrl.set_get_label_on_move_cb([this](int pos) { return m_show_estimated_times ? get_label(pos, ltEstimatedTime) : ""; });
m_ctrl.set_get_label_on_move_cb([this](int pos) {
m_pos_on_move = pos;
return m_show_estimated_times ? get_label(pos, ltEstimatedTime) : "";
});
m_ctrl.set_extra_draw_cb([this](const ImRect& draw_rc) {return draw_ticks(draw_rc); });
m_ticks.set_values(&m_values);
@ -170,22 +173,25 @@ using namespace ImGui;
void DSForLayers::draw_ticks(const ImRect& slideable_region)
{
//if(m_draw_mode != dmRegular)
// return;
//if (m_ticks.empty() || m_mode == MultiExtruder)
// return;
if (m_show_ruler)
draw_ruler(slideable_region);
if (m_ticks.empty() || m_draw_mode == dmSlaPrint)
return;
const ImVec2 tick_border = ImVec2(23.0f, 2.0f) * m_scale;
// distance form center begin end
const ImVec2 tick_size = ImVec2(19.0f, 11.0f) * m_scale;
const float tick_width = 1.0f * m_scale;
const ImVec2 tick_border = ImVec2(23.0f, 2.0f) * m_scale;
const float inner_x = 11.f * m_scale;
const float outer_x = 19.f * m_scale;
const float x_center = slideable_region.GetCenter().x;
const float tick_width = float(int(1.0f * m_scale + 0.5f));
const float icon_side = m_imgui->GetTextureCustomRect(ImGui::PausePrint)->Height;
const float icon_offset = 0.5f * icon_side;;
const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK);
const ImU32 tick_hovered_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND);
const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(m_show_ruler ? ImGuiPureWrap::COL_ORANGE_LIGHT : ImGuiPureWrap::COL_ORANGE_DARK);
const ImU32 tick_hovered_clr = ImGui::ColorConvertFloat4ToU32(m_show_ruler ? ImGuiPureWrap::COL_ORANGE_DARK : ImGuiPureWrap::COL_WINDOW_BACKGROUND);
auto get_tick_pos = [this, slideable_region](int tick) {
return m_ctrl.GetPositionInRect(tick, slideable_region);
@ -197,8 +203,8 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region)
float tick_pos = get_tick_pos(tick_it->tick);
//draw tick hover box when hovered
ImRect tick_hover_box = ImRect(slideable_region.GetCenter().x - tick_border.x, tick_pos - tick_border.y,
slideable_region.GetCenter().x + tick_border.x, tick_pos + tick_border.y - tick_width);
ImRect tick_hover_box = ImRect(x_center - tick_border.x, tick_pos - tick_border.y,
x_center + tick_border.x, tick_pos + tick_border.y - tick_width);
if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max)) {
ImGui::RenderFrame(tick_hover_box.Min, tick_hover_box.Max, tick_hovered_clr, false);
@ -219,12 +225,12 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region)
float tick_pos = get_tick_pos(tick_it->tick);
//draw ticks
ImRect tick_left = ImRect(slideable_region.GetCenter().x - tick_size.x, tick_pos - tick_width, slideable_region.GetCenter().x - tick_size.y, tick_pos);
ImRect tick_right = ImRect(slideable_region.GetCenter().x + tick_size.y, tick_pos - tick_width, slideable_region.GetCenter().x + tick_size.x, tick_pos);
ImRect tick_left = ImRect(x_center - outer_x, tick_pos - tick_width, x_center - inner_x, tick_pos);
ImRect tick_right = ImRect(x_center + inner_x, tick_pos - tick_width, x_center + outer_x, tick_pos);
ImGui::RenderFrame(tick_left.Min, tick_left.Max, tick_clr, false);
ImGui::RenderFrame(tick_right.Min, tick_right.Max, tick_clr, false);
ImVec2 icon_pos = ImVec2(tick_right.Max.x + 0.5f * icon_offset, tick_pos - icon_offset);
ImVec2 icon_pos = ImVec2(m_ctrl.GetCtrlPos().x + GetWidth(), tick_pos - icon_offset);
std::string btn_label = "tick " + std::to_string(tick_it->tick);
//draw tick icon-buttons
@ -257,6 +263,174 @@ void DSForLayers::draw_ticks(const ImRect& slideable_region)
}
}
void DSForLayers::draw_ruler(const ImRect& slideable_region)
{
if (m_values.empty())
return;
const double step = double(slideable_region.GetHeight()) / (m_ctrl.GetMaxPos() - m_ctrl.GetMinPos());
if (!m_ruler.valid())
m_ruler.init(m_values, step);
const float inner_x = 11.f * m_scale;
const float long_outer_x = 17.f * m_scale;
const float short_outer_x = 14.f * m_scale;
const float tick_width = float(int(1.0f * m_scale +0.5f));
const float label_height = m_imgui->GetTextureCustomRect(ImGui::PausePrint)->Height;
const ImU32 tick_clr = IM_COL32(255, 255, 255, 255);
const float x_center = slideable_region.GetCenter().x;
double max_val = 0.;
for (const auto& val : m_ruler.max_values)
if (max_val < val)
max_val = val;
if (m_show_ruler_bg) {
// draw ruler BG
ImRect bg_rect = slideable_region;
bg_rect.Expand(ImVec2(0.f, long_outer_x));
bg_rect.Min.x -= tick_width;
bg_rect.Max.x = m_ctrl.GetCtrlPos().x + GetWidth();
bg_rect.Min.y = m_ctrl.GetCtrlPos().y + label_height;
bg_rect.Max.y = m_ctrl.GetCtrlPos().y + GetHeight() - label_height;
const ImU32 bg_color = ImGui::ColorConvertFloat4ToU32(ImVec4(0.13f, 0.13f, 0.13f, 0.5f));
ImGui::RenderFrame(bg_rect.Min, bg_rect.Max, bg_color, false, 2.f * m_ctrl.rounding());
}
auto get_tick_pos = [this, slideable_region](int tick) -> float {
return m_ctrl.GetPositionInRect(tick, slideable_region);
};
auto draw_text = [max_val, x_center, label_height, long_outer_x, this](const int tick, const float tick_pos)
{
ImVec2 start = ImVec2(x_center + long_outer_x + 1, tick_pos - (0.5f * label_height));
std::string label = get_label(tick, ltHeight, max_val > 100.0 ? "%1$.1f" : "%1$.2f");
ImGui::RenderText(start, label.c_str());
};
auto draw_tick = [tick_clr, x_center, tick_width, inner_x](const float tick_pos, const float outer_x)
{
ImRect tick_right = ImRect(x_center + inner_x, tick_pos - tick_width, x_center + outer_x, tick_pos);
ImGui::RenderFrame(tick_right.Min, tick_right.Max, tick_clr, false);
};
auto draw_short_ticks = [this, short_outer_x, draw_tick, get_tick_pos](double& current_tick, int max_tick)
{
if (m_ruler.short_step <= 0.0)
return;
while (current_tick < max_tick) {
float pos = get_tick_pos(lround(current_tick));
draw_tick(pos, short_outer_x);
current_tick += m_ruler.short_step;
if (current_tick > m_ctrl.GetMaxPos())
break;
}
};
double short_tick = NaNd;
int tick = 0;
double value = 0.0;
size_t sequence = 0;
int prev_y_pos = -1;
int values_size = (int)m_values.size();
if (m_ruler.long_step < 0) {
// sequential print when long_step wasn't detected because of a lot of printed objects
if (m_ruler.max_values.size() > 1) {
while (tick <= m_ctrl.GetMaxPos() && sequence < m_ruler.count()) {
// draw just ticks with max value
value = m_ruler.max_values[sequence];
short_tick = tick;
for (; tick < values_size; tick++) {
if (m_values[tick] == value)
break;
if (m_values[tick] > value) {
if (tick > 0)
tick--;
break;
}
}
if (tick > m_ctrl.GetMaxPos())
break;
float pos = get_tick_pos(tick);
draw_tick(pos, long_outer_x);
if (prev_y_pos < 0 || prev_y_pos - pos >= label_height) {
draw_text(tick, pos);
prev_y_pos = pos;
}
draw_short_ticks(short_tick, tick);
sequence++;
tick++;
}
}
// very short object or some non-trivial ruler with non-regular step (see https://github.com/prusa3d/PrusaSlicer/issues/7263)
else {
if (step < 1) // step less then 1 px indicates very tall object with non-regular laayer step (probably in vase mode)
return;
for (size_t tick = 1; tick < m_values.size(); tick++) {
float pos = get_tick_pos(tick);
draw_tick(pos, long_outer_x);
draw_text(tick, pos);
}
}
}
else {
while (tick <= m_ctrl.GetMaxPos()) {
value += m_ruler.long_step;
if (sequence < m_ruler.count() && value > m_ruler.max_values[sequence])
value = m_ruler.max_values[sequence];
short_tick = tick;
for (; tick < values_size; tick++) {
if (m_values[tick] == value)
break;
if (m_values[tick] > value) {
if (tick > 0)
tick--;
break;
}
}
if (tick > m_ctrl.GetMaxPos())
break;
float pos = get_tick_pos(tick);
draw_tick(pos, long_outer_x);
if (prev_y_pos < 0 || prev_y_pos - pos >= label_height) {
draw_text(tick, pos);
prev_y_pos = pos;
}
draw_short_ticks(short_tick, tick);
if (sequence < m_ruler.count() && value == m_ruler.max_values[sequence]) {
value = 0.0;
sequence++;
tick++;
}
}
// short ticks from the last tick to the end
draw_short_ticks(short_tick, m_ctrl.GetMaxPos());
}
// draw mose move line
if (m_pos_on_move > 0) {
float line_pos = get_tick_pos(m_pos_on_move);
ImRect move_line = ImRect(x_center + 0.75f * inner_x, line_pos - tick_width, x_center + 1.5f * long_outer_x, line_pos);
ImGui::RenderFrame(move_line.Min, move_line.Max, ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_LIGHT), false);
m_pos_on_move = -1;
}
}
static std::array<float, 4> decode_color_to_float_array(const std::string color)
{
auto hex_digit_to_int = [](const char c) {
@ -504,7 +678,7 @@ bool DSForLayers::render_multi_extruders_menu(bool switch_current_code/* = false
void DSForLayers::render_color_picker()
{
ImGuiContext& context = *GImGui;
const std::string title = _u8L("Select color for Color Change");
const std::string title = ("Select color for Color Change");
if (m_show_color_picker) {
ImGuiPureWrap::set_next_window_pos(1200, 200, ImGuiCond_Always, 0.5f, 0.0f);
@ -534,12 +708,31 @@ void DSForLayers::render_cog_menu()
}
if (ImGuiPureWrap::menu_item_with_icon(_u8L("Show estimated print time on hover").c_str(), "", icon_sz, 0, m_show_estimated_times)) {
m_show_estimated_times = !m_show_estimated_times;
if (m_cb_change_app_config)
m_cb_change_app_config("show_estimated_times_in_dbl_slider", m_show_estimated_times ? "1" : "0");
}
if (m_mode == MultiAsSingle && m_draw_mode == dmRegular &&
ImGuiPureWrap::menu_item_with_icon(_u8L("Set extruder sequence for the entire print").c_str(), "")) {
if (m_ticks.edit_extruder_sequence(m_ctrl.GetMaxPos(), m_mode))
process_ticks_changed();
}
if (ImGuiPureWrap::begin_menu(_u8L("Ruler").c_str())) {
if (ImGuiPureWrap::menu_item_with_icon(_u8L("Show").c_str(), "", icon_sz, 0, m_show_ruler)) {
m_show_ruler = !m_show_ruler;
if (m_show_ruler)
m_imgui->set_requires_extra_frame();
if (m_cb_change_app_config)
m_cb_change_app_config("show_ruler_in_dbl_slider", m_show_ruler ? "1" : "0");
}
if (ImGuiPureWrap::menu_item_with_icon(_u8L("Show backgroung").c_str(), "", icon_sz, 0, m_show_ruler_bg)) {
m_show_ruler_bg = !m_show_ruler_bg;
if (m_cb_change_app_config)
m_cb_change_app_config("show_ruler_bg_in_dbl_slider", m_show_ruler_bg ? "1" : "0");
}
ImGuiPureWrap::end_menu();
}
if (can_edit()) {
if (ImGuiPureWrap::menu_item_with_icon(_u8L("Use default colors").c_str(), "", icon_sz, 0, m_ticks.used_default_colors())) {
UseDefaultColors(!m_ticks.used_default_colors());
@ -707,11 +900,15 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float
return;
m_scale = extra_scale * 0.1f * m_em;
m_ruler.set_scale(m_scale);
const float action_btn_sz = m_imgui->GetTextureCustomRect(ImGui::DSRevert)->Height;
const float tick_icon_side = m_imgui->GetTextureCustomRect(ImGui::PausePrint)->Height;
ImVec2 pos;
const float VERTICAL_SLIDER_WIDTH = m_show_ruler ? 125.f : 105.0f;
pos.x = canvas_width - VERTICAL_SLIDER_WIDTH * m_scale - tick_icon_side;
pos.y = 1.5f * action_btn_sz + offset;
if (m_allow_editing)
@ -719,7 +916,7 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float
ImVec2 size = ImVec2(VERTICAL_SLIDER_WIDTH * m_scale, canvas_height - 4.f * action_btn_sz - offset);
m_ctrl.Init(pos, size, m_scale);
m_ctrl.Init(pos, size, m_scale, m_show_ruler);
if (m_ctrl.render()) {
// request one more frame if value was changes with mouse wheel
if (GImGui->IO.MouseWheel != 0.0f)
@ -770,6 +967,12 @@ void DSForLayers::Render(const int canvas_width, const int canvas_height, float
render_color_picker();
}
void DSForLayers::force_ruler_update()
{
if (m_show_ruler)
m_ruler.invalidate();
}
bool DSForLayers::is_wipe_tower_layer(int tick) const
{
if (!m_ticks.is_wipe_tower || tick >= (int)m_values.size())
@ -824,7 +1027,7 @@ static std::string short_and_splitted_time(const std::string& time)
return get_s();
}
std::string DSForLayers::get_label(int pos, LabelType label_type) const
std::string DSForLayers::get_label(int pos, LabelType label_type, const std::string& fmt/* = "%1$.2f"*/) const
{
const size_t value = pos;
@ -861,7 +1064,7 @@ std::string DSForLayers::get_label(int pos, LabelType label_type) const
}
return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : "";
}
std::string str = format("%1$.2f", m_values[value]);
std::string str = format(fmt, m_values[value]);
if (label_type == ltHeight)
return str;
if (label_type == ltHeightWithLayer) {

View File

@ -6,6 +6,7 @@
#define slic3r_GUI_DoubleSliderForLayers_hpp_
#include "ImGuiDoubleSlider.hpp"
#include "RulerForDoubleSlider.hpp"
#include "TickCodesManager.hpp"
#include <vector>
@ -72,6 +73,7 @@ public:
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder);
void Render(const int canvas_width, const int canvas_height, float extra_scale = 1.f, float offset = 0.f) override;
void force_ruler_update();
// jump to selected layer
void jump_to_value();
@ -82,6 +84,8 @@ public:
void UseDefaultColors(bool def_colors_on);
bool is_new_print(const std::string& print_obj_idxs);
void set_imgui_wrapper(Slic3r::GUI::ImGuiWrapper* imgui) { m_imgui = imgui; }
void show_estimated_times(bool show) { m_show_estimated_times = show; }
void show_ruler(bool show, bool show_bg) { m_show_ruler = show; m_show_ruler_bg = show_bg; }
// manipulation with slider from keyboard
@ -104,6 +108,9 @@ public:
void set_callback_on_get_print (std::function<const Slic3r::Print& ()> cb)
{ m_cb_get_print = cb; }
void set_callback_on_change_app_config (std::function<void(const std::string&, const std::string&)> cb)
{ m_cb_change_app_config = cb; }
void set_callback_on_empty_auto_color_change(std::function<void()> cb)
{ m_ticks.set_callback_on_empty_auto_color_change(cb); }
@ -134,14 +141,18 @@ private:
bool is_osx { false };
bool m_allow_editing { true };
bool m_show_estimated_times { false };
bool m_show_estimated_times { true };
bool m_show_ruler { false };
bool m_show_ruler_bg { true };
bool m_show_cog_menu { false };
bool m_show_edit_menu { false };
int m_pos_on_move { -1 };
DrawMode m_draw_mode { dmRegular };
Mode m_mode { SingleExtruder };
FocusedItem m_focus { fiNone };
Ruler m_ruler;
TickCodeManager m_ticks;
Slic3r::GUI::ImGuiWrapper* m_imgui { nullptr };
@ -150,7 +161,7 @@ private:
bool is_wipe_tower_layer(int tick) const;
std::string get_label(int tick, LabelType label_type) const;
std::string get_label(int tick, LabelType label_type, const std::string& fmt = "%1$.2f") const;
std::string get_tooltip(int tick = -1);
@ -160,6 +171,7 @@ private:
void draw_colored_band(const ImRect& groove, const ImRect& slideable_region);
void draw_ticks(const ImRect& slideable_region);
void draw_ruler(const ImRect& slideable_region);
void render_menu();
void render_cog_menu();
void render_edit_menu();
@ -195,6 +207,7 @@ private:
std::function<void()> m_cb_ticks_changed { nullptr };
std::function<std::vector<std::string>()> m_cb_get_extruder_colors { nullptr };
std::function<const Slic3r::Print&()> m_cb_get_print { nullptr };
std::function<void(const std::string&, const std::string&)> m_cb_change_app_config { nullptr };
};
} // DoubleSlider;

View File

@ -6008,7 +6008,7 @@ bool GLCanvas3D::check_toolbar_icon_size(float init_scale, float& new_scale_to_s
const float top_tb_width = m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar.get_width();
float items_cnt = float(m_main_toolbar.get_visible_items_cnt() + m_undoredo_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt());
const float noitems_width = top_tb_width - float(size) * items_cnt; // width of separators and borders in top toolbars
items_cnt += 1.6; // +1.6 means a place for some minimal margin between toolbars
items_cnt += 1.6f; // +1.6 means a place for some minimal margin between toolbars
// calculate scale needed for items in all top toolbars
// the std::max() is there because on some Linux dialects/virtual machines this code is called when the canvas has not been properly initialized yet,

View File

@ -360,6 +360,7 @@ void Preview::hide_layers_slider()
void Preview::on_size(wxSizeEvent& evt)
{
evt.Skip();
m_layers_slider->force_ruler_update();
Refresh();
}
@ -393,12 +394,18 @@ void Preview::create_sliders()
m_layers_slider = std::make_unique<DoubleSlider::DSForLayers>(0, 0, 0, 100, wxGetApp().is_editor());
m_layers_slider->SetEmUnit(wxGetApp().em_unit());
m_layers_slider->set_imgui_wrapper(wxGetApp().imgui());
m_layers_slider->show_estimated_times(wxGetApp().app_config->get_bool("show_estimated_times_in_dbl_slider"));
m_layers_slider->show_ruler(wxGetApp().app_config->get_bool("show_ruler_in_dbl_slider"), wxGetApp().app_config->get_bool("show_ruler_bg_in_dbl_slider"));
m_layers_slider->SetDrawMode(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA,
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_bool("complete_objects"));
m_layers_slider->set_callback_on_thumb_move( [this]() -> void { Preview::on_layers_slider_scroll_changed(); } );
m_layers_slider->set_callback_on_change_app_config([this](const std::string& key, const std::string& val) -> void {
wxGetApp().app_config->set(key, val);
});
if (wxGetApp().is_editor()) {
m_layers_slider->set_callback_on_ticks_changed([this]() -> void {
Model& model = wxGetApp().plater()->model();
@ -605,6 +612,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
//first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
m_layers_slider->SetExtruderColors(plater->get_extruder_color_strings_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
m_layers_slider->SetSliderValues(layers_z);
m_layers_slider->force_ruler_update();
assert(m_layers_slider->GetMinPos() == 0);
m_layers_slider->Freeze();

View File

@ -80,6 +80,43 @@ static bool behavior(ImGuiID id, const ImRect& region,
return value_changed;
}
static bool lclicked_on_thumb(ImGuiID id, const ImRect& region,
const ImS32 v_min, const ImS32 v_max,
const ImRect& thumb, ImGuiSliderFlags flags = 0)
{
ImGuiContext& context = *GImGui;
if (context.ActiveId == id && context.ActiveIdSource == ImGuiInputSource_Mouse &&
context.IO.MouseReleased[0])
{
const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
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;
ImS32 v_new = v_min + (ImS32)(v_range * mouse_pos_ratio + 0.5f);
// Output thumb position so it can be displayed by the caller
const ImS32 v_clamped = (v_min < v_max) ? ImClamp(v_new, v_min, v_max) : ImClamp(v_new, 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(thumb.GetCenter().x, thumb_pos) : ImVec2(thumb_pos, thumb.GetCenter().y);
if (thumb.Contains(new_thumb_center))
return true;
}
return false;
}
ImRect ImGuiControl::DrawOptions::groove(const ImVec2& pos, const ImVec2& size, bool is_horizontal) const
{
ImVec2 groove_start = is_horizontal ?
@ -262,6 +299,7 @@ bool ImGuiControl::IsLClickOnThumb()
if (m_lclick_on_selected_thumb) {
// discard left mouse click at list its value is checked to avoud reuse it on next frame
m_lclick_on_selected_thumb = false;
m_suppress_process_behavior = false;
return true;
}
return false;
@ -493,8 +531,25 @@ bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
ImGui::ItemHoverable(m_regions.lower_thumb, id) && context.IO.MouseClicked[0])
m_selection = ssLower;
// detect left click on selected thumb
{
const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb;
if (ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseClicked[0]) {
m_active_thumb = active_thumb;
m_suppress_process_behavior = true;
}
else if (ImGui::ItemHoverable(active_thumb, id) && context.IO.MouseReleased[0]) {
const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region;
if (lclicked_on_thumb(id, slideable_region, m_min_pos, m_max_pos, m_active_thumb, m_flags)) {
m_suppress_process_behavior = true;
m_lclick_on_selected_thumb = true;
}
}
}
// update thumb position
bool pos_changed = false;
if (!m_suppress_process_behavior) {
if (m_selection == ssHigher) {
pos_changed = behavior(id, m_regions.higher_slideable_region, m_min_pos, m_max_pos,
higher_pos, &m_regions.higher_thumb, m_flags);
@ -506,16 +561,25 @@ bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
// check thumbs poss and correct them if needed
check_and_correct_thumbs(higher_pos, lower_pos);
}
const ImRect& slideable_region = m_selection == ssHigher ? m_regions.higher_slideable_region : m_regions.lower_slideable_region;
const ImRect& active_thumb = m_selection == ssHigher ? m_regions.higher_thumb : m_regions.lower_thumb;
bool show_move_label = false;
ImRect mouse_pos_rc = active_thumb;
if (!pos_changed && ImGui::ItemHoverable(item_size, id) && !ImGui::IsMouseDragging(0)) {
behavior(id, slideable_region, m_min_pos, m_max_pos,
std::string move_label = "";
auto move_size = item_size;
move_size.Min.x += left_dummy_sz().x;
if (!pos_changed && ImGui::ItemHoverable(move_size, id) && !ImGui::IsMouseDragging(0)) {
auto sl_region = slideable_region;
if (!is_horizontal() && m_draw_opts.has_ruler)
sl_region.Max.x += m_draw_opts.dummy_sz().x;
behavior(id, sl_region, m_min_pos, m_max_pos,
&m_mouse_pos, &mouse_pos_rc, m_flags, true);
show_move_label = true;
move_label = get_label_on_move(m_mouse_pos);
}
// detect right click on selected thumb
@ -525,16 +589,8 @@ bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
context.IO.MouseClicked[0])
m_rclick_on_selected_thumb = false;
// detect left click on selected thumb
if (ImGui::ItemHoverable(active_thumb, id) && !pos_changed) {
ImVec2 active_thumb_center = active_thumb.GetCenter();
if (context.IO.MouseClicked[0])
m_active_thumb_center_on_lcklick = active_thumb_center;
if (context.IO.MouseReleased[0] &&
(m_active_thumb_center_on_lcklick.y == active_thumb_center.y) &&
(m_active_thumb_center_on_lcklick.x == active_thumb_center.x) )
m_lclick_on_selected_thumb = true;
}
if (m_suppress_process_behavior && ImGui::ItemHoverable(item_size, id) && ImGui::IsMouseDragging(0))
m_suppress_process_behavior = false;
// render slider
@ -565,8 +621,7 @@ bool ImGuiControl::draw_slider( int* higher_pos, int* lower_pos,
}
// draw label on mouse move
if (show_move_label)
draw_label(get_label_on_move(m_mouse_pos), mouse_pos_rc, false, true);
draw_label(move_label, mouse_pos_rc, false, true);
return pos_changed;
}

View File

@ -62,10 +62,11 @@ public:
void SetCtrlPos(ImVec2 pos) { m_pos = pos; }
void SetCtrlSize(ImVec2 size) { m_size = size; }
void SetCtrlScale(float scale) { m_draw_opts.scale = scale; }
void Init(const ImVec2& pos, const ImVec2& size, float scale) {
void Init(const ImVec2& pos, const ImVec2& size, float scale, bool has_ruler = false) {
m_pos = pos;
m_size = size;
m_draw_opts.scale = scale;
m_draw_opts.has_ruler = has_ruler;
}
ImVec2 GetCtrlSize() { return m_size; }
ImVec2 GetCtrlPos() { return m_pos; }
@ -90,6 +91,8 @@ public:
bool render();
std::string get_label(int pos) const;
float rounding() const { return m_draw_opts.rounding(); }
ImVec2 left_dummy_sz() const { return m_draw_opts.text_dummy_sz() + m_draw_opts.text_padding(); }
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; }
@ -100,12 +103,13 @@ private:
struct DrawOptions {
float scale { 1.f }; // used for Retina on osx
bool has_ruler { false };
ImVec2 dummy_sz() const { return ImVec2(24.0f, 16.0f) * scale; }
ImVec2 dummy_sz() const { return ImVec2(has_ruler ? 48.0f : 24.0f, 16.0f) * scale; }
ImVec2 thumb_dummy_sz() const { return ImVec2(17.0f, 17.0f) * scale; }
ImVec2 groove_sz() const { return ImVec2(4.0f, 4.0f) * scale; }
ImVec2 draggable_region_sz()const { return ImVec2(20.0f, 19.0f) * scale; }
ImVec2 text_dummy_sz() const { return ImVec2(50.0f, 34.0f) * scale; }
ImVec2 text_dummy_sz() const { return ImVec2(60.0f, 34.0f) * scale; }
ImVec2 text_padding() const { return ImVec2( 5.0f, 2.0f) * scale; }
float thumb_radius() const { return 10.0f * scale; }
@ -140,7 +144,8 @@ private:
bool m_rclick_on_selected_thumb{ false };
bool m_lclick_on_selected_thumb{ false };
ImVec2 m_active_thumb_center_on_lcklick;
bool m_suppress_process_behavior{ false };
ImRect m_active_thumb;
bool m_draw_lower_thumb{ true };
bool m_combine_thumbs { false };

View File

@ -0,0 +1,96 @@
///|/ 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 "RulerForDoubleSlider.hpp"
#include "libslic3r/CustomGCode.hpp"
using namespace Slic3r;
using namespace CustomGCode;
namespace DoubleSlider {
static const double PIXELS_PER_SM_DEFAULT = 96./*DEFAULT_DPI*/ * 5. / 25.4;
void Ruler::init(const std::vector<double>& values, double scroll_step)
{
if (m_is_valid)
return;
max_values.clear();
max_values.reserve(std::count(values.begin(), values.end(), values.front()));
auto it = std::find(values.begin() + 1, values.end(), values.front());
while (it != values.end()) {
max_values.push_back(*(it - 1));
it = std::find(it + 1, values.end(), values.front());
}
max_values.push_back(*(it - 1));
m_is_valid = true;
update(values, scroll_step);
}
void Ruler::update(const std::vector<double>& values, double scroll_step)
{
if (!m_is_valid || values.empty() ||
// check if need to update ruler in respect to input values
(values.front() == m_min_val && values.back() == m_max_val && m_scroll_step == scroll_step && max_values.size() == m_max_values_cnt))
return;
m_min_val = values.front();
m_max_val = values.back();
m_scroll_step = scroll_step;
m_max_values_cnt = max_values.size();
int pixels_per_sm = lround(m_scale * PIXELS_PER_SM_DEFAULT);
if (lround(scroll_step) > pixels_per_sm) {
long_step = -1.0;
return;
}
int pow = -2;
int step = 0;
auto end_it = std::find(values.begin() + 1, values.end(), values.front());
while (pow < 3) {
for (int istep : {1, 2, 5}) {
double val = (double)istep * std::pow(10, pow);
auto val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
if (val_it == values.end())
break;
int tick = val_it - values.begin();
// find next tick with istep
val *= 2;
val_it = std::lower_bound(values.begin(), end_it, val - epsilon());
// count of short ticks between ticks
int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick;
if (lround(short_ticks_cnt * scroll_step) > pixels_per_sm) {
step = istep;
// there couldn't be more then 10 short ticks between ticks
short_step = 0.1 * short_ticks_cnt;
break;
}
}
if (step > 0)
break;
pow++;
}
long_step = step == 0 ? -1.0 : (double)step * std::pow(10, pow);
if (long_step < 0)
short_step = long_step;
}
void Ruler::set_scale(double scale)
{
if (!is_approx(m_scale, scale))
m_scale = scale;
}
} // DoubleSlider

View File

@ -0,0 +1,42 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_RulerForDoubleSlider_hpp_
#define slic3r_GUI_RulerForDoubleSlider_hpp_
#include <vector>
#include <set>
namespace DoubleSlider {
class Ruler
{
bool m_is_valid { false };
double m_scale { 1. };
double m_min_val;
double m_max_val;
double m_scroll_step;
size_t m_max_values_cnt;
public:
double long_step;
double short_step;
std::vector<double> max_values;// max value for each object/instance in sequence print
// > 1 for sequential print
void init(const std::vector<double>& values, double scroll_step);
void update(const std::vector<double>& values, double scroll_step);
void set_scale(double scale);
void invalidate() { m_is_valid = false; }
bool is_ok() { return long_step > 0 && short_step > 0; }
size_t count() { return max_values.size(); }
bool valid() { return m_is_valid; }
};
} // DoubleSlider;
#endif // slic3r_GUI_RulerForDoubleSlider_hpp_