diff --git a/resources/images/texture_displacement_video_guide.svg b/resources/images/texture_displacement_video_guide.svg index aeeaa8d40f..74a21032bf 100644 --- a/resources/images/texture_displacement_video_guide.svg +++ b/resources/images/texture_displacement_video_guide.svg @@ -1 +1 @@ - + diff --git a/resources/images/texture_displacement_wiki.svg b/resources/images/texture_displacement_wiki.svg index b614fa893e..6cbaeb18dd 100644 --- a/resources/images/texture_displacement_wiki.svg +++ b/resources/images/texture_displacement_wiki.svg @@ -1 +1 @@ - + diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 96b1458122..d8b047e4ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -3396,7 +3396,7 @@ void GLGizmoTextureDisplacement::ensure_panel_icons() "texture_displacement_adjust.svg", "canvas_drag.svg", "texture_displacement_move_up.svg", "texture_displacement_move_down.svg", "texture_displacement_drag.svg", "texture_displacement_select_all.svg", "texture_displacement_erase_all.svg", - // Header help links: the video walkthrough and the wiki page. + // Help links on the action row: the wiki page and the video walkthrough. "texture_displacement_video_guide.svg", "texture_displacement_wiki.svg", }; std::vector paths; @@ -5441,6 +5441,47 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float m_imgui->tooltip(tip_text, wrap_w); return clicked; }; + // Round link button: a white glyph on a solid teal disc, for the help links on the action row. + // ImGui clamps an image button's frame rounding to its frame padding, so the disc cannot be the + // button's own background; it is drawn underneath on a separate draw-list channel, which is what + // lets it be filled in after the button has reported its hover state. + const auto link_button = [&](int uid, const std::string &iconfile, float sz, const wxString &label, + const wxString &tip_text) -> bool { + const auto it = m_panel_icon_map.find(iconfile); + const float pad = std::max(1.f, std::round(sz * 0.26f)); + ImDrawList *dl = ImGui::GetWindowDrawList(); + bool clicked = false; + dl->ChannelsSplit(2); + dl->ChannelsSetCurrent(1); + ImGui::PushID(uid); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.f, 0.f, 0.f, 0.f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.f, 0.f, 0.f, 0.f)); + if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) { + const IconManager::Icon &ic = *it->second[0]; + clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz - 2.f * pad, sz - 2.f * pad), + ic.tl, ic.br, int(pad)); + } else { + clicked = ImGui::Button(label.ToUTF8().data(), ImVec2(0.f, sz)); + } + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(); + ImGui::PopID(); + // The disc is the button's whole surface, so it has to carry the hover feedback that the + // transparent ImGui background no longer gives: lighter under the cursor, darker while held. + const bool hovered = ImGui::IsItemHovered(); + const float lift = ImGui::IsItemActive() ? -0.12f : hovered ? 0.18f : 0.f; + const auto shade = [lift](float c) { return lift >= 0.f ? c + (1.f - c) * lift : c * (1.f + lift); }; + const ImVec2 lo = ImGui::GetItemRectMin(), hi = ImGui::GetItemRectMax(); + dl->ChannelsSetCurrent(0); + dl->AddCircleFilled(ImVec2(0.5f * (lo.x + hi.x), 0.5f * (lo.y + hi.y)), 0.5f * (hi.y - lo.y), + ImGui::GetColorU32(ImVec4(shade(orca.x), shade(orca.y), shade(orca.z), orca.w))); + dl->ChannelsMerge(); + if (!tip_text.empty() && hovered) + m_imgui->tooltip(tip_text, wrap_w); + return clicked; + }; // A short vertical rule between groups of buttons on one row; call it right after an item. const auto vsep = [&](float h) { ImGui::SameLine(0.f, gap_s); @@ -5509,10 +5550,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float const float seg_pad = m_imgui->scaled(0.5f); const float seg_w[2] = { ImGui::CalcTextSize(labels[0].c_str()).x + 2.f * seg_pad, ImGui::CalcTextSize(labels[1].c_str()).x + 2.f * seg_pad }; ImGui::SameLine(); - // Three icons now follow the segmented control (video guide, wiki, dock toggle), each preceded by - // its own gap - the reserved width has to cover all of them or the cluster runs past the panel edge. - ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), - x0 + panel_w - (seg_w[0] + seg_w[1] + 3.f * (gap_s + icon_sm)))); + ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + panel_w - (seg_w[0] + seg_w[1] + gap_s + icon_sm))); // Standard / Pro is a mode, not an option: Standard hides every mesh-preparation control and folds // the whole recipe into Bake, Pro shows all of it and hands the ordering to the user. @@ -5554,15 +5592,6 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float ImGui::GetWindowDrawList()->AddRect(seg_min, ImVec2(ImGui::GetItemRectMax().x, seg_min.y + frame_h), ImGui::GetColorU32(col_frame), style.FrameRounding); - ImGui::SameLine(0.f, gap_s); - if (icon_button(807, "texture_displacement_video_guide.svg", icon_sm, _L("Video guide"), - _L("Watch the texture displacement walkthrough on YouTube. Opens in your browser."))) - wxLaunchDefaultBrowser("https://www.youtube.com/watch?v=D7w3tG1kdvE"); - ImGui::SameLine(0.f, gap_s); - if (icon_button(808, "texture_displacement_wiki.svg", icon_sm, _L("Documentation"), - _L("Open the texture displacement page of the OrcaSlicer wiki. Opens in your browser."))) - wxLaunchDefaultBrowser("https://www.orcaslicer.com/wiki/print_prepare/prepare_texture_displacement.html"); - ImGui::SameLine(0.f, gap_s); if (icon_button(806, "canvas_drag.svg", icon_sm, m_undocked ? _L("Dock panel") : _L("Undock panel"), _L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it back beside " @@ -6904,7 +6933,22 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float } const float button_h = std::round(frame_h * 1.25f); - const float third = std::floor((panel_w - style.ItemSpacing.x) / 3.f); + + // The help links lead the action row rather than sitting in the header: this row is the part of + // the panel that is always on screen, so a user who does not yet know the guide exists still + // passes over it on the way to Bake. + if (link_button(807, "texture_displacement_wiki.svg", button_h, _L("Documentation"), + _L("Open the texture displacement page of the OrcaSlicer wiki. Opens in your browser."))) + wxLaunchDefaultBrowser("https://www.orcaslicer.com/wiki/print_prepare/prepare_texture_displacement.html"); + ImGui::SameLine(0.f, gap_s); + if (link_button(808, "texture_displacement_video_guide.svg", button_h, _L("Video guide"), + _L("Watch the texture displacement walkthrough on YouTube. Opens in your browser."))) + wxLaunchDefaultBrowser("https://www.youtube.com/watch?v=D7w3tG1kdvE"); + ImGui::SameLine(); + + // Close and Bake keep their 1:2 split, over whatever the links left of the row. + const float links_w = 2.f * button_h + gap_s + style.ItemSpacing.x; + const float third = std::floor((panel_w - links_w - style.ItemSpacing.x) / 3.f); if (busy) { if (ImGui::Button((_u8L("Stop") + "##stop").c_str(), ImVec2(third, button_h))) wxGetApp().plater()->get_ui_job_worker().cancel_all();