mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 03:32:02 +08:00
ImguiDoubleSliser: WIP: Some of code is ported from BambuStudio (state of 96707fc4b4b40c30b7e5610d2489ef283fe952a4)
* Code improvements : + added render_button() + added draw_hexagon()
This commit is contained in:
parent
a5322ef970
commit
a26d17d689
@ -153,6 +153,8 @@ namespace ImGui
|
||||
const wchar_t PlugMarker = 0x1C;
|
||||
const wchar_t DowelMarker = 0x1D;
|
||||
const wchar_t SnapMarker = 0x1E;
|
||||
const wchar_t Lock = 0x1F;
|
||||
const wchar_t Unlock = 0x17;
|
||||
const wchar_t HorizontalHide = 0xB1;
|
||||
const wchar_t HorizontalShow = 0xB2;
|
||||
// Do not forget use following letters only in wstring
|
||||
|
@ -37,17 +37,39 @@
|
||||
|
||||
#include "ImGuiPureWrap.hpp"
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using GUI::from_u8;
|
||||
using GUI::into_u8;
|
||||
using GUI::format_wxstr;
|
||||
using GUI::format;
|
||||
|
||||
using namespace GUI;
|
||||
|
||||
namespace DoubleSlider {
|
||||
|
||||
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;
|
||||
@ -563,32 +585,690 @@ void Control::render()
|
||||
}
|
||||
}
|
||||
|
||||
void Control::imgui_render(GUI::GLCanvas3D& canvas)
|
||||
{
|
||||
GUI::Size cnv_size = canvas.get_canvas_size();
|
||||
ImVec2 mouse_pos = ImGui::GetMousePos();
|
||||
const float width = get_min_size().x*5;
|
||||
const float height = float(cnv_size.get_height());
|
||||
|
||||
ImVec2 win_pos(1.0f * (float)cnv_size.get_width(), 1.0f);
|
||||
ImGuiPureWrap::set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f);
|
||||
ImGuiPureWrap::set_next_window_size(width, height - 2.f, ImGuiCond_Always);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
|
||||
|
||||
// name of window indentifies window - has to be unique string
|
||||
std::string name = "DblSlider";
|
||||
|
||||
int flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing;
|
||||
|
||||
if (ImGuiPureWrap::begin(name, flags)) {
|
||||
// ImGuiDS
|
||||
|
||||
float Control::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 handle_pos;
|
||||
if (is_horizontal()) {
|
||||
handle_pos = rect.Min.x + (rect.Max.x - rect.Min.x) * pos_ratio;
|
||||
}
|
||||
ImGuiPureWrap::end();
|
||||
else {
|
||||
pos_ratio = 1.0f - pos_ratio;
|
||||
handle_pos = rect.Min.y + (rect.Max.y - rect.Min.y) * pos_ratio;
|
||||
}
|
||||
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;
|
||||
//if (m_ticks.empty() || m_mode == MultiExtruder)
|
||||
// return;
|
||||
if (m_ticks.empty())
|
||||
return;
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
|
||||
ImVec2 tick_box = ImVec2(46.0f, 16.0f) * m_scale;
|
||||
ImVec2 tick_offset = ImVec2(19.0f, 11.0f) * m_scale;
|
||||
float tick_width = 1.0f * m_scale;
|
||||
ImVec2 icon_offset = ImVec2(13.0f, 7.0f) * m_scale;
|
||||
ImVec2 icon_size = ImVec2(14.0f, 14.0f) * m_scale;
|
||||
|
||||
const ImU32 tick_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_ORANGE_DARK); //IM_COL32(144, 144, 144, 255);
|
||||
const ImU32 tick_hover_box_clr = ImGui::ColorConvertFloat4ToU32(ImGuiPureWrap::COL_WINDOW_BACKGROUND); //IM_COL32(219, 253, 231, 255);
|
||||
const ImU32 delete_btn_clr = IM_COL32(144, 144, 144, 255);
|
||||
|
||||
auto get_tick_pos = [this, slideable_region](int tick)
|
||||
{
|
||||
int v_min = GetMinValue();
|
||||
int v_max = GetMaxValue();
|
||||
return get_pos_from_value(v_min, v_max, tick, slideable_region);
|
||||
};
|
||||
|
||||
std::set<TickCode>::const_iterator tick_it = m_ticks.ticks.begin();
|
||||
while (tick_it != m_ticks.ticks.end())
|
||||
{
|
||||
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_box.x / 2, tick_pos - tick_box.y / 2, slideable_region.GetCenter().x + tick_box.x / 2,
|
||||
tick_pos + tick_box.y / 2);
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
tick_it = m_ticks.ticks.begin();
|
||||
while (tick_it != m_ticks.ticks.end())
|
||||
{
|
||||
float tick_pos = get_tick_pos(tick_it->tick);
|
||||
|
||||
//draw ticks
|
||||
ImRect tick_left = ImRect(slideable_region.GetCenter().x - tick_offset.x, tick_pos - tick_width, slideable_region.GetCenter().x - tick_offset.y, tick_pos);
|
||||
ImRect tick_right = ImRect(slideable_region.GetCenter().x + tick_offset.y, tick_pos - tick_width, slideable_region.GetCenter().x + tick_offset.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);
|
||||
|
||||
//draw pause icon
|
||||
if (tick_it->type == PausePrint) {
|
||||
//ImTextureID pause_icon_id = m_pause_icon_id;
|
||||
ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, tick_pos - icon_offset.y);
|
||||
//button_with_pos(pause_icon_id, icon_size, icon_pos);
|
||||
if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) {
|
||||
if (context.IO.MouseClicked[0])
|
||||
int a = 0;
|
||||
}
|
||||
}
|
||||
++tick_it;
|
||||
}
|
||||
|
||||
tick_it = m_selection == ssHigher ? m_ticks.ticks.find(TickCode{ this->GetHigherValue() }) :
|
||||
m_selection == ssLower ? m_ticks.ticks.find(TickCode{ this->GetLowerValue() }) :
|
||||
m_ticks.ticks.end();
|
||||
if (tick_it != m_ticks.ticks.end()) {
|
||||
// draw delete icon
|
||||
//ImTextureID delete_icon_id = m_delete_icon_id;
|
||||
ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, get_tick_pos(tick_it->tick) - icon_offset.y);
|
||||
//! button_with_pos(m_delete_icon_id, icon_size, icon_pos);
|
||||
if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) {
|
||||
if (context.IO.MouseClicked[0]) {
|
||||
// delete tick
|
||||
Type type = tick_it->type;
|
||||
m_ticks.ticks.erase(tick_it);
|
||||
post_ticks_changed_event(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline int hex_to_int(const char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') ? int(c - '0') : (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
|
||||
}
|
||||
|
||||
static std::array<float, 4> decode_color_to_float_array(const std::string color)
|
||||
{
|
||||
// set alpha to 1.0f by default
|
||||
std::array<float, 4> ret = { 0, 0, 0, 1.0f };
|
||||
const char* c = color.data() + 1;
|
||||
if (color.size() == 7 && color.front() == '#') {
|
||||
for (size_t j = 0; j < 3; ++j) {
|
||||
int digit1 = hex_to_int(*c++);
|
||||
int digit2 = hex_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1) break;
|
||||
ret[j] = float(digit1 * 16 + digit2) / 255.0f;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Control::draw_colored_band(const ImRect& groove, const ImRect& slideable_region) {
|
||||
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;
|
||||
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);
|
||||
|
||||
ImRect main_band = ImRect(blank_rect);
|
||||
main_band.Expand(blank_padding);
|
||||
|
||||
auto draw_band = [](const ImU32& clr, const ImRect& band_rc)
|
||||
{
|
||||
ImGui::RenderFrame(band_rc.Min, band_rc.Max, clr, false, band_rc.GetWidth() * 0.5);
|
||||
//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) {
|
||||
ImGui::RenderFrame(main_band.Min, main_band.Max, clr, false, main_band.GetWidth() * 0.5);
|
||||
};
|
||||
//draw main colored band
|
||||
const int default_color_idx = m_mode == MultiAsSingle ? std::max<int>(m_only_extruder - 1, 0) : 0;
|
||||
std::array<float, 4>rgba = decode_color_to_float_array(m_extruder_colors[default_color_idx]);
|
||||
ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f);
|
||||
draw_main_band(band_clr);
|
||||
|
||||
static float tick_pos;
|
||||
std::set<TickCode>::const_iterator tick_it = m_ticks.ticks.begin();
|
||||
while (tick_it != m_ticks.ticks.end())
|
||||
{
|
||||
//get position from tick
|
||||
tick_pos = get_pos_from_value(GetMinValue(), GetMaxValue(), tick_it->tick, slideable_region);
|
||||
|
||||
ImRect band_rect = ImRect(ImVec2(main_band.Min.x, std::min(tick_pos, main_band.Min.y)),
|
||||
ImVec2(main_band.Max.x, std::min(tick_pos, main_band.Max.y)));
|
||||
|
||||
if (main_band.Contains(band_rect)) {
|
||||
//draw colored band
|
||||
if (tick_it->type == ToolChange) {
|
||||
if ((m_mode == SingleExtruder) || (m_mode == MultiAsSingle)) {
|
||||
const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it);
|
||||
if (!clr_str.empty()) {
|
||||
std::array<float, 4>rgba = decode_color_to_float_array(clr_str);
|
||||
ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f);
|
||||
if (tick_it->tick == 0)
|
||||
draw_main_band(band_clr);
|
||||
else
|
||||
draw_band(band_clr, band_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tick_it->type == ColorChange/* && m_mode == SingleExtruder*/) {
|
||||
const std::string clr_str = m_mode == SingleExtruder ? tick_it->color : get_color_for_tool_change_tick(tick_it);
|
||||
if (!clr_str.empty()) {
|
||||
std::array<float, 4>rgba = decode_color_to_float_array(clr_str);
|
||||
ImU32 band_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f);
|
||||
if (tick_it->tick == 0)
|
||||
draw_main_band(band_clr);
|
||||
else
|
||||
draw_band(band_clr, band_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
tick_it++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, ImGui::GetStyle().ItemSpacing.y });
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_::ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
|
||||
std::vector<std::string> colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
|
||||
int extruder_num = colors.size();
|
||||
|
||||
if (m_show_menu) {
|
||||
ImGui::OpenPopup("slider_menu_popup");
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale);
|
||||
if (ImGui::BeginPopup("slider_menu_popup")) {
|
||||
if ((m_selection == ssLower && GetLowerValueD() == 0.0) || (m_selection == ssHigher && GetHigherValueD() == 0.0))
|
||||
{
|
||||
menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (menu_item_with_icon(_u8L("Add Color Change").c_str(), "")) {
|
||||
add_code_as_tick(ColorChange);
|
||||
}
|
||||
if (menu_item_with_icon(_u8L("Add Pause").c_str(), "")) {
|
||||
add_code_as_tick(PausePrint);
|
||||
}
|
||||
if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "")) {
|
||||
|
||||
}
|
||||
if (!gcode(Template).empty()) {
|
||||
if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "")) {
|
||||
add_code_as_tick(Template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//BBS render this menu item only when extruder_num > 1
|
||||
if (extruder_num > 1) {
|
||||
if (!m_can_change_color || m_draw_mode == dmSequentialFffPrint) {
|
||||
begin_menu(_u8L("Change Filament").c_str(), false);
|
||||
}
|
||||
else if (begin_menu(_u8L("Change Filament").c_str())) {
|
||||
for (int i = 0; i < extruder_num; i++) {
|
||||
std::array<float, 4> rgba = decode_color_to_float_array(colors[i]);
|
||||
ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f);
|
||||
if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1);
|
||||
}
|
||||
end_menu();
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar(1);
|
||||
|
||||
ImGui::PopStyleColor(1);
|
||||
ImGui::PopStyleVar(3);
|
||||
}
|
||||
|
||||
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 });
|
||||
|
||||
const ImVec4 col = { 0.25f, 0.25f, 0.25f, 0.0f };
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, col);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, col);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, col);
|
||||
|
||||
std::string btn_label;
|
||||
btn_label += btn_icon;
|
||||
const bool ret = ImGui::Button((btn_label + "##" + label_id).c_str(), ImVec2(16*scale, 0));
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
m_focus = focus;
|
||||
std::string tooltip = into_u8(get_tooltip());
|
||||
ImGuiPureWrap::tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool Control::imgui_render(GUI::GLCanvas3D& canvas)
|
||||
{
|
||||
bool result = false;
|
||||
GUI::Size cnv_size = canvas.get_canvas_size();
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
}
|
||||
else {
|
||||
float pos_x = canvas_width - (VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale;
|
||||
float pos_y = ONE_LAYER_OFFSET.y;
|
||||
ImVec2 size = ImVec2((VERTICAL_SLIDER_SIZE.x + TEXT_WIDTH_DUMMY * scale - TEXT_WIDTH_DUMMY) * m_scale, canvas_height - 4 * pos_y);
|
||||
|
||||
ImGuiPureWrap::set_next_window_pos(pos_x, pos_y, ImGuiCond_Always);
|
||||
ImGuiPureWrap::begin(std::string("laysers_slider"), windows_flag);
|
||||
|
||||
if (!m_ticks.empty() && /*m_draw_mode == dmRegular && */render_button(ImGui::RevertButton, "revert", fiRevertIcon))
|
||||
discard_all_thicks();
|
||||
else
|
||||
ImGui::NewLine();
|
||||
|
||||
render_menu();
|
||||
|
||||
int higher_value = GetHigherValue();
|
||||
int lower_value = GetLowerValue();
|
||||
std::string higher_label = into_u8(get_label(m_higher_value));
|
||||
std::string lower_label = into_u8(get_label(m_lower_value));
|
||||
int temp_higher_value = higher_value;
|
||||
int temp_lower_value = lower_value;
|
||||
|
||||
if (vertical_slider("laysers_slider", &higher_value, &lower_value, higher_label, lower_label, GetMinValue(), GetMaxValue(),
|
||||
ImVec2(pos_x, pos_y), size, m_selection, is_one_layer(), scale)) {
|
||||
if (temp_higher_value != higher_value)
|
||||
SetHigherValue(higher_value);
|
||||
if (temp_lower_value != lower_value)
|
||||
SetLowerValue(lower_value);
|
||||
result = true;
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
if (render_button(is_one_layer() ? ImGui::Lock : ImGui::Unlock, "one_layer", fiOneLayerIcon))
|
||||
switch_one_layer_mode();
|
||||
|
||||
ImGui::NewLine();
|
||||
if (m_draw_mode != dmSequentialGCodeView && render_button(ImGui::PrintIconMarker, "settings", fiCogIcon))
|
||||
show_cog_icon_context_menu();
|
||||
|
||||
ImGuiPureWrap::end();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Control::is_wipe_tower_layer(int tick) const
|
||||
@ -743,10 +1423,10 @@ static wxString short_and_splitted_time(const std::string& time)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
|
||||
// Format the dhm time.
|
||||
auto get_d = [days]() { return format(_u8L("%1%d"), days); };
|
||||
auto get_h = [hours]() { return format(_u8L("%1%h"), hours); };
|
||||
auto get_m = [minutes](){ return format(_u8L("%1%m"), minutes); };
|
||||
auto get_s = [seconds](){ return format(_u8L("%1%s"), seconds); };
|
||||
auto get_d = [days]() { return GUI::format(_u8L("%1%d"), days); };
|
||||
auto get_h = [hours]() { return GUI::format(_u8L("%1%h"), hours); };
|
||||
auto get_m = [minutes](){ return GUI::format(_u8L("%1%m"), minutes); };
|
||||
auto get_s = [seconds](){ return GUI::format(_u8L("%1%s"), seconds); };
|
||||
|
||||
if (days > 0)
|
||||
return format_wxstr("%1%%2%\n%3%", get_d(), get_h(), get_m());
|
||||
|
@ -303,7 +303,7 @@ public:
|
||||
void show_cog_icon_context_menu();
|
||||
void auto_color_change();
|
||||
|
||||
void imgui_render(GUI::GLCanvas3D& canvas);
|
||||
bool imgui_render(GUI::GLCanvas3D& canvas);
|
||||
|
||||
protected:
|
||||
|
||||
@ -476,6 +476,25 @@ private:
|
||||
size_t count() { return max_values.size(); }
|
||||
bool can_draw() { return m_parent != nullptr; }
|
||||
} m_ruler;
|
||||
|
||||
// ImGuiDS
|
||||
float m_scale{ 1.0 };
|
||||
bool m_can_change_color{ true };
|
||||
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);
|
||||
|
||||
};
|
||||
|
||||
} // DoubleSlider;
|
||||
|
@ -352,6 +352,8 @@ 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())
|
||||
m_moves_slider->imgui_render(canvas);
|
||||
}
|
||||
|
||||
void Preview::jump_layers_slider(wxKeyEvent& evt)
|
||||
|
@ -296,6 +296,20 @@ bool combo(const std::string& label, const std::vector<std::string>& options, in
|
||||
return res;
|
||||
}
|
||||
|
||||
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
|
||||
float a_min = start_angle;
|
||||
float a_max = start_angle + 2.f * IM_PI;
|
||||
|
||||
window->DrawList->PathArcTo(center, radius, a_min, a_max, 6);
|
||||
window->DrawList->PathFillConvex(col);
|
||||
}
|
||||
|
||||
// Scroll up for one item
|
||||
void scroll_up()
|
||||
{
|
||||
|
@ -53,6 +53,8 @@ namespace ImGuiPureWrap
|
||||
// Use selection = -1 to not mark any option as selected
|
||||
bool combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
|
||||
|
||||
void draw_hexagon(const ImVec2& center, float radius, ImU32 col, float start_angle = 0.f);
|
||||
|
||||
void text(const char* label);
|
||||
void text(const std::string& label);
|
||||
void text(const std::wstring& label);
|
||||
|
@ -1523,6 +1523,308 @@ 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();
|
||||
if (window->SkipItems) return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
bool menu_is_open = ImGui::IsPopupOpen(id, ImGuiPopupFlags_None);
|
||||
|
||||
// Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
|
||||
if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) flags |= ImGuiWindowFlags_ChildWindow;
|
||||
|
||||
// If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
|
||||
// We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
|
||||
// If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.
|
||||
if (g.MenusIdSubmittedThisFrame.contains(id)) {
|
||||
if (menu_is_open)
|
||||
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
else
|
||||
g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values
|
||||
return menu_is_open;
|
||||
}
|
||||
|
||||
// Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
|
||||
g.MenusIdSubmittedThisFrame.push_back(id);
|
||||
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
|
||||
bool pressed;
|
||||
bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) &&
|
||||
(g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back());
|
||||
ImGuiWindow* backed_nav_window = g.NavWindow;
|
||||
if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent)
|
||||
|
||||
// The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
|
||||
// However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
|
||||
// e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
|
||||
ImVec2 popup_pos, pos = window->DC.CursorPos;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
|
||||
// Menu inside an horizontal menu bar
|
||||
// Selectable extend their highlight by half ItemSpacing in each direction.
|
||||
// For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
|
||||
popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
float w = label_size.x;
|
||||
pressed = selectable(label, menu_is_open,
|
||||
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
|
||||
(!enabled ? ImGuiSelectableFlags_Disabled : 0),
|
||||
ImVec2(w, 0.0f));
|
||||
ImGui::PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(
|
||||
style.ItemSpacing.x *
|
||||
(-1.0f +
|
||||
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else {
|
||||
// Menu inside a menu
|
||||
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
||||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame
|
||||
float extra_w = ImMax(0.0f, ImGui::GetContentRegionAvail().x - min_w);
|
||||
pressed = selectable(label, menu_is_open,
|
||||
ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups |
|
||||
ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0),
|
||||
ImVec2(min_w, 0.0f));
|
||||
ImU32 text_col = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
||||
ImGui::RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right);
|
||||
}
|
||||
|
||||
const bool hovered = enabled && ImGui::ItemHoverable(window->DC.LastItemRect, id);
|
||||
if (menuset_is_open) g.NavWindow = backed_nav_window;
|
||||
|
||||
bool want_open = false;
|
||||
bool want_close = false;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
|
||||
{
|
||||
// Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
|
||||
// Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
|
||||
bool moving_toward_other_child_menu = false;
|
||||
|
||||
ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ?
|
||||
g.OpenPopupStack[g.BeginPopupStack.Size].Window :
|
||||
NULL;
|
||||
if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) {
|
||||
// FIXME-DPI: Values should be derived from a master "scale" factor.
|
||||
ImRect next_window_rect = child_menu_window->Rect();
|
||||
ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta;
|
||||
ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR();
|
||||
ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR();
|
||||
float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack.
|
||||
ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues
|
||||
tb.y = ta.y +
|
||||
ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale?
|
||||
tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f);
|
||||
moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
|
||||
// GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
|
||||
}
|
||||
if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu)
|
||||
want_close = true;
|
||||
|
||||
if (!menu_is_open && hovered && pressed) // Click to open
|
||||
want_open = true;
|
||||
else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open
|
||||
want_open = true;
|
||||
|
||||
if (g.NavActivateId == id) {
|
||||
want_close = menu_is_open;
|
||||
want_open = !menu_is_open;
|
||||
}
|
||||
if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
|
||||
{
|
||||
want_open = true;
|
||||
ImGui::NavMoveRequestCancel();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Menu bar
|
||||
if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
|
||||
{
|
||||
want_close = true;
|
||||
want_open = menu_is_open = false;
|
||||
}
|
||||
else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
|
||||
{
|
||||
want_open = true;
|
||||
}
|
||||
else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
|
||||
{
|
||||
want_open = true;
|
||||
ImGui::NavMoveRequestCancel();
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
|
||||
want_close = true;
|
||||
if (want_close && ImGui::IsPopupOpen(id, ImGuiPopupFlags_None)) ImGui::ClosePopupToLevel(g.BeginPopupStack.Size, true);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
|
||||
if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) {
|
||||
// Don't recycle same menu level in the same frame, first close the other menu and yield for a frame.
|
||||
ImGui::OpenPopup(label);
|
||||
return false;
|
||||
}
|
||||
|
||||
menu_is_open |= want_open;
|
||||
if (want_open) ImGui::OpenPopup(label);
|
||||
|
||||
if (menu_is_open) {
|
||||
ImGui::SetNextWindowPos(popup_pos,
|
||||
ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
|
||||
menu_is_open = ImGui::BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
|
||||
}
|
||||
else {
|
||||
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
|
||||
}
|
||||
|
||||
return menu_is_open;
|
||||
}
|
||||
|
||||
void end_menu()
|
||||
{
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
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*/)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems) return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiStyle& style = g.Style;
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
|
||||
|
||||
// We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
|
||||
// but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
|
||||
ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled);
|
||||
bool pressed;
|
||||
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) {
|
||||
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
|
||||
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
|
||||
float w = label_size.x;
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
pressed = ImGui::Selectable(label, selected, flags, ImVec2(w, 0.0f));
|
||||
ImGui::PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(
|
||||
style.ItemSpacing.x *
|
||||
(-1.0f +
|
||||
0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else {
|
||||
// Menu item inside a vertical menu
|
||||
// (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
||||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f;
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame
|
||||
float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w);
|
||||
pressed = selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
|
||||
|
||||
if (icon_size.x != 0 && icon_size.y != 0) {
|
||||
float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y;
|
||||
float icon_pos_y = selectable_pos_y + (label_size.y + style.ItemSpacing.y - icon_size.y) / 2;
|
||||
float icon_pos_x = pos.x + window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f;
|
||||
ImVec2 icon_pos = ImVec2(icon_pos_x, icon_pos_y);
|
||||
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
|
||||
}
|
||||
|
||||
if (shortcut_w > 0.0f) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
|
||||
ImGui::RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (selected) {
|
||||
//ImGui::RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f),
|
||||
// ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f);
|
||||
}
|
||||
}
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.LastItemStatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
@ -22,6 +22,7 @@ struct OptionViewParameters;
|
||||
class wxString;
|
||||
class wxMouseEvent;
|
||||
class wxKeyEvent;
|
||||
struct ImRect;
|
||||
|
||||
struct IMGUI_API ImGuiWindow;
|
||||
|
||||
@ -153,6 +154,11 @@ 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);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
Loading…
x
Reference in New Issue
Block a user