diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 051a7ee40..3e83418ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -220,6 +220,7 @@ void GLGizmoMeasure::data_changed() m_last_plane_idx = -1; m_selected_features.reset(); m_selection_raycasters.clear(); + m_editing_distance = false; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -256,6 +257,7 @@ void GLGizmoMeasure::on_set_state() m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); + m_editing_distance = false; } else { m_mode = EMode::BasicSelection; @@ -840,18 +842,79 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string value_str = format_double(value); + const std::string units = use_inches ? _u8L("in") : _u8L("mm"); + const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + static double edit_value = 0.0; + const Vec2d label_position = 0.5 * (v1ss + v2ss); m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); + + if (!m_editing_distance) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + ImGui::AlignTextToFramePadding(); + m_imgui->text(value_str + " " + units); + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = value; + m_imgui->requires_extra_frame(); + } + m_imgui->end(); + ImGui::PopStyleVar(3); + } + + auto perform_scale = [this, value](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + + if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) + ImGui::OpenPopup("distance_popup"); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : - format_double(distance) + " " + _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); + if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + ImGui::PushItemWidth(value_str_width); + if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Scale"))) { + perform_scale(edit_value, value); + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Cancel"))) { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::PopStyleVar(4); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1194,7 +1257,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit static float last_y = 0.0f; static float last_h = 0.0f; - m_imgui->begin(_L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (m_editing_distance) + return; + + m_imgui->begin(_u8L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); @@ -1341,28 +1407,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text_colored(col_2_color, col_2); ImGui::TableSetColumnIndex(2); - ImGuiIO& io = ImGui::GetIO(); - const ImTextureID tex_id = io.Fonts->TexID; - assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); - float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); - float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); - const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); - const ImVec2 size = { float(rect->Width), float(rect->Height) }; - const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); - const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); - ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); - if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + if (m_imgui->image_button(ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { wxTheClipboard->Open(); wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); wxTheClipboard->Close(); } - ImGui::PopStyleColor(3); - if (ImGui::IsItemHovered()) { - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); - } }; if (m_selected_features.second.feature.has_value()) { @@ -1373,7 +1422,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - ImGui::PushID((void*)(intptr_t)1); + ImGui::PushID("ClipboardAngle"); add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1382,7 +1431,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)2); + ImGui::PushID("ClipboardDistanceInfinite"); add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1392,7 +1441,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)3); + ImGui::PushID("ClipboardDistanceStrict"); add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1401,7 +1450,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)4); + ImGui::PushID("ClipboardDistanceXYZ"); add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 7e65b8c89..72f9098f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -118,6 +118,7 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; SelectedFeatures m_selected_features; + bool m_editing_distance{ false }; void update_if_needed(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b8c6c463f..f1be9d277 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -426,36 +426,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a return pressed; } -bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) -{ - return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); -} - -bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format) -{ - auto label_utf8 = into_u8(label); - return input_double(label_utf8, value, format); -} - -bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format) -{ - bool value_changed = false; - - ImGui::BeginGroup(); - - for (int i = 0; i < 3; ++i) - { - std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z"); - ImGui::PushID(i); - ImGui::PushItemWidth(width); - value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast(&value(i)), 0.0f, 0.0f, format.c_str()); - ImGui::PopID(); - } - ImGui::EndGroup(); - - return value_changed; -} - bool ImGuiWrapper::checkbox(const wxString &label, bool &value) { auto label_utf8 = into_u8(label); @@ -514,20 +484,20 @@ void ImGuiWrapper::text_wrapped(const wxString &label, float wrap_width) void ImGuiWrapper::tooltip(const char *label, float wrap_width) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8.0f, 8.0f }); ImGui::BeginTooltip(); ImGui::PushTextWrapPos(wrap_width); ImGui::TextUnformatted(label); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); + ImGui::PopStyleVar(3); } void ImGuiWrapper::tooltip(const wxString &label, float wrap_width) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(wrap_width); - ImGui::TextUnformatted(label.ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + tooltip(label.ToUTF8().data(), wrap_width); } ImVec2 ImGuiWrapper::get_slider_icon_size() const @@ -669,6 +639,29 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); } +bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip) +{ + const ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f }); + const bool res = image_button(tex_id, size, uv0, uv1); + ImGui::PopStyleColor(3); + + if (!tooltip.empty() && ImGui::IsItemHovered()) + this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + + return res; +} + bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags) { // this is to force the label to the left of the widget: diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 769deccb8..dc5f5517c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -89,9 +89,6 @@ public: bool button(const wxString& label, float width, float height); bool radio_button(const wxString &label, bool active); bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); - bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); - bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); - bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool checkbox(const wxString &label, bool &value); void text(const char *label); void text(const std::string &label); @@ -112,6 +109,7 @@ public: bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + bool image_button(const wchar_t icon, const wxString& tooltip = L""); // Use selection = -1 to not mark any option as selected bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0);