Unify Tool UI Footer (#12850)

* unify footer
adds "Done" Buttons,
standardize "Reset" Buttons,
adds disabled logic to "Reset" Buttons

* adds ImGuiWrapper::COL_WARNING = ColorRGB:Warning,
replaces ColorRGB::Warning

* consistently display warnings,
moves assermbly warning after footer,
make cut warnings warning color

* adds separator above cut warnings

* adds tooltip entry in move, rotate & scale for auto-drop

* use wrapper tooltip in wrapper->button for consistent text color

* restructures brim ears button,
renames "Reset" button in painting tools to "Erase All",
small styling fixes

* small adjustments,
moves "Edge detection" checkbox in painting tool underneath,
moves "Section View" in Brim ears tool after buttons,
moves warnings in Assemble tool under footer

* small brim ear tool structure changes,
adds separator between "Head Diameter" and "Max angle",
adds tooltip to "Auto-generate" button,
rewords shortcut descriptions to match other tools

---------

Co-authored-by: Hanno Witzleb <hannowitzleb@gmail.com>
This commit is contained in:
Hanno Witzleb
2026-05-18 06:58:58 +02:00
committed by GitHub
parent 15451c6c50
commit 3ca7c4b752
18 changed files with 549 additions and 289 deletions

View File

@@ -226,7 +226,7 @@ bool GLCanvas3D::LayersEditing::is_allowed() const
float GLCanvas3D::LayersEditing::s_overlay_window_width;
void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanvas3D& canvas) {
void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(GLCanvas3D& canvas) {
if (!m_enabled)
return;
@@ -336,8 +336,23 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv
GLGizmoUtils::render_tooltip_button(&imgui, canvas, shortcuts, x, y);
ImGui::SameLine();
if (imgui.button(_L("Reset")))
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
imgui.disabled_begin(check_object_layers_fixed(*m_slicing_parameters, m_layer_height_profile));
if (imgui.button(_L("Reset"))) {
wxPostEvent((wxEvtHandler*) canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
}
imgui.disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (imgui.button(_L("Done"))) {
m_enabled = false;
GLToolbarItem* item = canvas.m_main_toolbar.get_item("layersediting");
item->set_state(GLToolbarItem::Normal);
canvas.set_as_dirty();
canvas.request_extra_frame();
}
GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x;
imgui.end();
@@ -345,7 +360,7 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv
imgui.pop_toolbar_style();
}
void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas)
void GLCanvas3D::LayersEditing::render_overlay(GLCanvas3D& canvas)
{
render_variable_layer_height_dialog(canvas);
render_active_object_annotations(canvas);

View File

@@ -290,8 +290,8 @@ class GLCanvas3D
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enabled) { m_enabled = is_allowed() && enabled; }
void render_variable_layer_height_dialog(const GLCanvas3D& canvas);
void render_overlay(const GLCanvas3D& canvas);
void render_variable_layer_height_dialog(GLCanvas3D& canvas);
void render_overlay(GLCanvas3D& canvas);
void render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes);
void adjust_layer_height_profile();

View File

@@ -104,20 +104,24 @@ void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limi
}
show_selection_ui();
show_face_face_assembly_common();
ImGui::Separator();
show_face_face_assembly_senior();
show_distance_xyz_ui();
render_input_window_warning(m_same_model_object);
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::PopStyleVar(2);
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({ _L("Done") });
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
render_input_window_warning(m_same_model_object);
if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) {
// the dialog may have changed its size, ask for an extra frame to render it properly
@@ -127,21 +131,40 @@ void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limi
m_imgui->set_requires_extra_frame();
}
m_last_active_item_imgui = m_current_active_imgui_id;
GizmoImguiEnd();
// Orca
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
ImGuiWrapper::pop_toolbar_style();
}
void GLGizmoAssembly::render_input_window_warning(bool same_model_object)
{
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) {
if (m_hit_different_volumes.size() == 2) {
if (same_model_object == false) {
m_imgui->warning_text(_L("Warning") + ": " +
_L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted."));
}
const bool same_mesh_warning = m_hit_different_volumes.size() == 1;
const bool wrong_feature_warning = m_selected_wrong_feature_waring_tip;
const bool not_assembled_warning = m_hit_different_volumes.size() == 2 && same_model_object == false &&
wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D;
if (same_mesh_warning || not_assembled_warning || wrong_feature_warning) {
ImGui::Separator();
}
if (same_mesh_warning) {
m_imgui->warning_text(_L("Warning: please select two different meshes."));
}
if (wrong_feature_warning) {
if (m_assembly_mode == AssemblyMode::FACE_FACE) {
m_imgui->warning_text(_L("Warning: please select Plane's feature."));
} else if (m_assembly_mode == AssemblyMode::POINT_POINT) {
m_imgui->warning_text(_L("Warning: please select Point's or Circle's feature."));
}
}
if (not_assembled_warning) {
m_imgui->warning_text(
_L("Warning") + ": " +
_L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted.")
);
}
}
bool GLGizmoAssembly::render_assembly_mode_combo(double label_width, float item_width)

View File

@@ -48,19 +48,22 @@ bool GLGizmoBrimEars::on_init()
const wxString ctrl = GUI::shortkey_ctrl_prefix();
const wxString alt = GUI::shortkey_alt_prefix();
m_desc["head_diameter"] = _L("Head diameter");
m_desc["max_angle"] = _L("Max angle");
m_desc["detection_radius"] = _L("Detection radius");
m_desc["remove_selected"] = _L("Remove selected points");
m_desc["remove_all"] = _L("Remove all");
m_desc["auto_generate"] = _L("Auto-generate points");
m_desc["section_view"] = _L("Section view");
m_desc["head_diameter"] = _L("Head diameter");
m_desc["max_angle"] = _L("Max angle");
m_desc["detection_radius"] = _L("Detection radius");
m_desc["remove"] = _L("Remove");
m_desc["remove_selected"] = _L("Selected");
m_desc["remove_all"] = _L("All");
m_desc["create"] = _L("Create");
m_desc["auto_generate"] = _L("Auto-generate");
m_desc["auto-generate-tooltip"] = _L("Generate brim ears using Max angle and Detection radius");
m_desc["section_view"] = _L("Section view");
m_shortcuts = {
{_L("Left mouse button"), _L("Add a brim ear")},
{_L("Right mouse button"), _L("Delete a brim ear")},
{ctrl + _L("Mouse wheel"), _L("Adjust head diameter")},
{alt + _L("Mouse wheel"), _L("Adjust section view")},
{_L("Left mouse button"), _L("Add or Select")},
{_L("Right mouse button"), _L("Remove")},
{ctrl + _L("Mouse wheel"), m_desc["head_diameter"]},
{alt + _L("Mouse wheel"), m_desc["section_view"]},
};
return true;
@@ -545,6 +548,15 @@ void GLGizmoBrimEars::delete_selected_points()
update_model_object();
}
bool GLGizmoBrimEars::has_selected_points() const
{
for (const auto& entry : m_editing_cache) {
if (entry.selected)
return true;
}
return false;
}
void GLGizmoBrimEars::on_dragging(const UpdateData& data)
{
if (m_hover_id != -1) {
@@ -634,21 +646,21 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
y = std::min(y, bottom_limit - win_h);
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
const float currt_scale = m_parent.get_scale();
ImGuiWrapper::push_toolbar_style(currt_scale);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0 * currt_scale, 5.0 * currt_scale));
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale);
const float f_scale = m_parent.get_scale();
ImGuiWrapper::push_toolbar_style(f_scale);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GizmoImguiBegin(get_name(),
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
std::vector<wxString> text_list = {m_desc["head_diameter"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"]};
std::vector<wxString> text_list = {m_desc["head_diameter"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"],
m_desc["create"], m_desc["remove"]};
float widest_text = m_imgui->find_widest_text(text_list);
float caption_size = widest_text + space_size + ImGui::GetStyle().WindowPadding.x;
float input_text_size = m_imgui->scaled(10.0f);
float button_size = ImGui::GetFrameHeight();
float list_width = input_text_size + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale;
float list_width = input_text_size + ImGui::GetStyle().ScrollbarSize + 2 * f_scale;
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
const float slider_width = list_width - space_size;
@@ -662,12 +674,12 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
if (last_y != y) last_y = y;
}
ImGui::AlignTextToFramePadding();
// Following is a nasty way to:
// - save the initial value of the slider before one starts messing with it
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
ImGui::AlignTextToFramePadding();
float initial_value = m_new_point_head_diameter;
m_imgui->text(m_desc["head_diameter"]);
ImGui::SameLine(caption_size);
@@ -684,8 +696,10 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##head_diameter_input", &m_new_point_head_diameter, 0.05f, 0.0f, 0.0f, "%.1f");
ImGui::AlignTextToFramePadding();
ImGui::Separator();
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["max_angle"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
@@ -693,8 +707,8 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##max_angle_input", &m_max_angle, 0.05f, 0.0f, 180.0f, "%.1f");
ImGui::AlignTextToFramePadding();
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["detection_radius"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
@@ -702,8 +716,37 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##detection_radius_input", &m_detection_radius, 0.05f, 0.0f, static_cast<float>(m_detection_radius_max), "%.1f");
ImGui::Separator();
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["create"]);
ImGui::SameLine(caption_size);
if (m_imgui->button(m_desc["auto_generate"], m_desc["auto-generate-tooltip"])) {
auto_generate();
}
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["remove"]);
ImGui::SameLine(caption_size);
m_imgui->disabled_begin(has_selected_points() == false);
if (m_imgui->button(m_desc["remove_selected"])) {
delete_selected_points();
}
m_imgui->disabled_end();
ImGui::SameLine();
m_imgui->disabled_begin(m_editing_cache.empty());
if (m_imgui->button(m_desc["remove_all"])) {
if (m_editing_cache.size() > 0) {
select_point(AllPoints);
delete_selected_points();
}
}
m_imgui->disabled_end();
ImGui::Separator();
ImGui::AlignTextToFramePadding();
float clp_dist = float(m_c->object_clipper()->get_position());
m_imgui->text(m_desc["section_view"]);
ImGui::SameLine(caption_size);
@@ -712,79 +755,72 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##section_view_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, false, true); }
ImGui::Separator();
// ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
if (m_imgui->button(m_desc["auto_generate"])) { auto_generate(); }
if (m_imgui->button(m_desc["remove_selected"])) { delete_selected_points(); }
float font_size = ImGui::GetFontSize();
//ImGui::Dummy(ImVec2(font_size * 1, font_size * 1.3));
ImGui::SameLine();
if (m_imgui->button(m_desc["remove_all"])) {
if (m_editing_cache.size() > 0) {
select_point(AllPoints);
delete_selected_points();
}
if (slider_clp_dist || b_clp_dist_input) {
m_c->object_clipper()->set_position_by_ratio(clp_dist, false, true);
}
ImGui::PopStyleVar(1);
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
ImGui::Separator();
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
if (glb_cfg.opt_enum<BrimType>("brim_type") != btPainted) {
ImGui::SameLine();
auto link_text = [&]() {
ImColor HyperColor = ImGuiWrapper::COL_ORCA;
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING()));
float parent_width = ImGui::GetContentRegionAvail().x;
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
bool brim_not_painted = (obj_cfg.option("brim_type")) ? (obj_cfg.opt_enum<BrimType>("brim_type") != btPainted) :
(glb_cfg.opt_enum<BrimType>("brim_type") != btPainted);
bool has_invalid_ears = !m_single_brim.empty();
if (brim_not_painted || has_invalid_ears) {
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_WARNING);
float parent_width = ImGui::GetContentRegionAvail().x;
float font_size = ImGui::GetFontSize();
if (brim_not_painted) {
m_imgui->text_wrapped(_L("Warning: The brim type is not set to \"painted\", the brim ears will not take effect!"), parent_width);
ImGui::PopStyleColor();
ImColor HyperColor = ImGuiWrapper::COL_ORCA;
ImGui::PushStyleColor(ImGuiCol_Text, HyperColor.Value);
ImGui::Dummy(ImVec2(font_size * 1.8, font_size * 1.3));
ImGui::Dummy(ImVec2(font_size * 1.8f, 0.0f)); // Horizontal indent
ImGui::SameLine();
m_imgui->bold_text(_u8L("Set the brim type of this object to \"painted\""));
ImGui::PopStyleColor();
// underline
ImVec2 lineEnd = ImGui::GetItemRectMax();
lineEnd.y -= 2.0f;
ImVec2 lineStart = lineEnd;
lineStart.x = ImGui::GetItemRectMin().x;
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, HyperColor);
if (ImGui::IsMouseHoveringRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), true)) {
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 max = ImGui::GetItemRectMax();
ImGui::GetWindowDrawList()->AddLine(ImVec2(min.x, max.y - 2.0f), ImVec2(max.x, max.y - 2.0f), HyperColor);
if (ImGui::IsMouseHoveringRect(min, max)) {
m_link_text_hover = true;
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
DynamicPrintConfig new_conf = obj_cfg;
new_conf.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btPainted));
mo->config.assign_config(new_conf);
}
}else {
} else {
m_link_text_hover = false;
}
};
if (obj_cfg.option("brim_type")) {
if (obj_cfg.opt_enum<BrimType>("brim_type") != btPainted) {
link_text();
}
}else {
link_text();
ImGui::PopStyleColor(1); // Pop HyperColor
}
}
if (has_invalid_ears) {
wxString out = _L("Warning") + ": " + std::to_string(m_single_brim.size()) + " " + _L("invalid brim ears");
m_imgui->text_wrapped(out, parent_width);
}
if (!m_single_brim.empty()) {
wxString out = _L("Warning") + ": " + std::to_string(m_single_brim.size()) + _L(" invalid brim ears");
m_imgui->warning_text(out);
ImGui::PopStyleColor(1); // Pop Warning Color
} else {
// Reset hover state if no warnings are active
m_link_text_hover = false;
}
GizmoImguiEnd();
ImGui::PopStyleVar(2);
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
ImGuiWrapper::pop_toolbar_style();
}

View File

@@ -80,6 +80,7 @@ public:
bool on_mouse(const wxMouseEvent& mouse_event) override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points();
bool has_selected_points() const;
void update_model_object();
//ClippingPlane get_sla_clipping_plane() const;

View File

@@ -2369,7 +2369,10 @@ void GLGizmoCut3D::apply_selected_connectors(std::function<void(size_t idx)> app
void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, float x, float y, float bottom_limit)
{
// Connectors section
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::Separator();
// WIP : Auto : Need to implement
@@ -2447,26 +2450,24 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
render_tooltip_button(x, y);
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Confirm connectors"), _L("Cancel")});
GLGizmoUtils::push_orca_button_style();
if (m_imgui->button(_L("Confirm connectors"))) {
unselect_all_connectors();
set_connectors_editing(false);
}
GLGizmoUtils::pop_orca_button_style();
ImGui::SameLine(m_label_width + m_editing_window_width - m_imgui->calc_text_size(_L("Cancel")).x - m_imgui->get_style_scaling() * 8);
ImGui::SameLine();
if (m_imgui->button(_L("Cancel"))) {
reset_connectors();
set_connectors_editing(false);
}
ImGui::PopStyleVar(2);
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
}
void GLGizmoCut3D::render_build_size()
@@ -2846,10 +2847,14 @@ void GLGizmoCut3D::render_snap_specific_input(const std::string& label, const wx
void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, float x, float y, float bottom_limit)
{
// if (m_mode == size_t(CutMode::cutPlanar)) {
const bool has_connectors = !connectors.empty();
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center);
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
CutMode mode = CutMode(m_mode);
if (mode == CutMode::cutPlanar || mode == CutMode::cutTongueAndGroove) {
const bool has_connectors = !connectors.empty();
m_imgui->disabled_begin(has_connectors);
if (render_cut_mode_combo())
@@ -2864,7 +2869,6 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, floa
render_move_center_input(Z);
ImGui::SameLine();
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center.isApprox(m_plane_center);
m_imgui->disabled_begin(is_cut_plane_init);
std::string act_name = _u8L("Reset cutting plane");
if (render_reset_button("cut_plane", act_name)) {
@@ -2873,26 +2877,13 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, floa
}
m_imgui->disabled_end();
// render_flip_plane_button();
if (mode == CutMode::cutPlanar) {
add_vertical_scaled_interval(0.75f);
ImGui::Separator();
m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object()));
if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors")))
set_connectors_editing(true);
m_imgui->disabled_end();
ImGui::SameLine(1.5f * m_control_width);
m_imgui->disabled_begin(is_cut_plane_init && !has_connectors);
act_name = _u8L("Reset cut");
if (m_imgui->button(wxString::FromUTF8(act_name), _L("Reset cutting plane and remove connectors"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), act_name, UndoRedo::SnapshotType::GizmoAction);
reset_cut_plane();
reset_connectors();
}
m_imgui->disabled_end();
}
else if (mode == CutMode::cutTongueAndGroove) {
m_is_slider_editing_done = false;
@@ -2978,21 +2969,35 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors, floa
}
ImGui::Separator();
render_tooltip_button(x, y);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
render_tooltip_button(x, get_cur_y);
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
if (mode == CutMode::cutPlanar) {
ImGui::SameLine();
m_imgui->disabled_begin(is_cut_plane_init && !has_connectors);
if (m_imgui->button(_L("Reset"), _L("Reset cutting plane and remove connectors"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset Cut", UndoRedo::SnapshotType::GizmoAction);
reset_cut_plane();
reset_connectors();
}
m_imgui->disabled_end();
}
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Perform cut"), _L("Cancel")});
m_imgui->disabled_begin(!can_perform_cut());
if(m_imgui->button(_L("Perform cut")))
perform_cut(m_parent.get_selection());
GLGizmoUtils::push_orca_button_style();
if(m_imgui->button(_L("Perform cut")))
perform_cut(m_parent.get_selection());
GLGizmoUtils::pop_orca_button_style();
m_imgui->disabled_end();
ImGui::PopStyleVar(2);
ImGui::SameLine();
if (m_imgui->button(_L("Cancel"))) {
m_parent.reset_all_gizmos();
}
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
}
void GLGizmoCut3D::validate_connector_settings()
@@ -3095,7 +3100,16 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
void GLGizmoCut3D::render_input_window_warning() const
{
if (! m_invalid_connectors_idxs.empty()) {
const bool invalid_connector_warning = !m_invalid_connectors_idxs.empty();
const bool keep_after_cut_warning = !m_keep_upper && !m_keep_lower;
const bool invalid_contour_warning = !has_valid_contour();
const bool invalid_groove_warning = !has_valid_groove();
if (invalid_connector_warning || keep_after_cut_warning || invalid_contour_warning || invalid_groove_warning) {
ImGui::Separator();
}
if (invalid_connector_warning) {
wxString out = /*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Invalid connectors detected") + ":";
if (m_info_stats.outside_cut_contour > size_t(0))
out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of cut contour", "%1$d connectors are out of cut contour", m_info_stats.outside_cut_contour),
@@ -3105,14 +3119,14 @@ void GLGizmoCut3D::render_input_window_warning() const
m_info_stats.outside_bb);
if (m_info_stats.is_overlap)
out += "\n - " + _L("Some connectors are overlapped");
m_imgui->text(out);
m_imgui->warning_text(out);
}
if (!m_keep_upper && !m_keep_lower)
m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Select at least one object to keep after cutting."));
if (!has_valid_contour())
m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane is placed out of object"));
else if (!has_valid_groove())
m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane with groove is invalid"));
if (keep_after_cut_warning)
m_imgui->warning_text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Select at least one object to keep after cutting."));
if (invalid_contour_warning)
m_imgui->warning_text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane is placed out of object"));
else if (invalid_groove_warning)
m_imgui->warning_text(/*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Cut plane with groove is invalid"));
}
void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)

View File

@@ -1267,6 +1267,59 @@ void GLGizmoEmboss::reset_volume()
remove_notification_not_valid_font();
}
namespace {
// FIX IT: It should not change volume position before successfull change volume by process
void fix_transformation(const StyleManager::Style& from, const StyleManager::Style& to, GLCanvas3D& canvas)
{
// fix Z rotation when exists difference in styles
const std::optional<float>& f_angle_opt = from.angle;
const std::optional<float>& t_angle_opt = to.angle;
if (!is_approx(f_angle_opt, t_angle_opt)) {
// fix rotation
float f_angle = f_angle_opt.value_or(.0f);
float t_angle = t_angle_opt.value_or(.0f);
do_local_z_rotate(canvas.get_selection(), t_angle - f_angle);
std::string no_snapshot;
canvas.do_rotate(no_snapshot);
}
// fix distance (Z move) when exists difference in styles
const std::optional<float>& f_move_opt = from.distance;
const std::optional<float>& t_move_opt = to.distance;
if (!is_approx(f_move_opt, t_move_opt)) {
float f_move = f_move_opt.value_or(.0f);
float t_move = t_move_opt.value_or(.0f);
do_local_z_move(canvas.get_selection(), t_move - f_move);
std::string no_snapshot;
canvas.do_move(no_snapshot);
}
}
} // namespace
bool GLGizmoEmboss::is_changed_from_default_style()
{
int default_index = 0;
const StyleManager::Style& default_style = m_style_manager.get_styles()[default_index];
const StyleManager::Style& current_style = m_style_manager.get_style();
return default_style == current_style;
}
void GLGizmoEmboss::reset_to_default_style()
{
int default_index = 0;
const StyleManager::Style& current_style = m_style_manager.get_style();
const StyleManager::Style& default_style = m_style_manager.get_styles()[default_index];
StyleManager::Style cur_s = current_style; // copy
StyleManager::Style new_s = default_style; // copy
if (m_style_manager.load_style(default_index)) {
::fix_transformation(cur_s, new_s, m_parent);
process();
}
}
void GLGizmoEmboss::calculate_scale() {
Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix();
auto to_world_linear = to_world.linear();
@@ -1406,9 +1459,29 @@ void GLGizmoEmboss::draw_window(float x, float y)
draw_model_type();
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
ImGui::SameLine();
m_imgui->disabled_begin(is_changed_from_default_style());
if (m_imgui->button(_L("Reset"), _L("Reset all options except the text and operation"))) {
reset_to_default_style();
}
m_imgui->disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
#ifdef SHOW_WX_FONT_DESCRIPTOR
if (is_selected_style)
@@ -2155,34 +2228,6 @@ void GLGizmoEmboss::draw_delete_style_button() {
}
}
namespace {
// FIX IT: It should not change volume position before successfull change volume by process
void fix_transformation(const StyleManager::Style &from, const StyleManager::Style &to, GLCanvas3D &canvas) {
// fix Z rotation when exists difference in styles
const std::optional<float> &f_angle_opt = from.angle;
const std::optional<float> &t_angle_opt = to.angle;
if (!is_approx(f_angle_opt, t_angle_opt)) {
// fix rotation
float f_angle = f_angle_opt.value_or(.0f);
float t_angle = t_angle_opt.value_or(.0f);
do_local_z_rotate(canvas.get_selection(), t_angle - f_angle);
std::string no_snapshot;
canvas.do_rotate(no_snapshot);
}
// fix distance (Z move) when exists difference in styles
const std::optional<float> &f_move_opt = from.distance;
const std::optional<float> &t_move_opt = to.distance;
if (!is_approx(f_move_opt, t_move_opt)) {
float f_move = f_move_opt.value_or(.0f);
float t_move = t_move_opt.value_or(.0f);
do_local_z_move(canvas.get_selection(), t_move - f_move);
std::string no_snapshot;
canvas.do_move(no_snapshot);
}
}
} // namesapce
void GLGizmoEmboss::draw_style_list() {
if (!m_style_manager.is_active_font()) return;

View File

@@ -109,6 +109,9 @@ private:
void set_volume_by_selection();
void reset_volume();
bool is_changed_from_default_style();
void reset_to_default_style();
// create volume from text - main functionality
bool process(bool make_snapshot = true);
void close();

View File

@@ -82,7 +82,7 @@ bool GLGizmoFdmSupports::on_init()
m_desc["perform"] = _L("Perform");
m_desc["on_overhangs_only"] = _L("On highlighted overhangs only");
m_desc["remove_all"] = _L("Erase all painting");
m_desc["remove_all"] = _L("Erase all");
m_desc["highlight_by_angle"] = _L("Highlight overhang areas");
m_desc["tool_type"] = _L("Tool type");
m_desc["gap_fill"] = _L("Gap fill");
@@ -215,6 +215,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
//BBS
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
@@ -225,11 +228,10 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
const float highlight_slider_left = m_imgui->calc_text_size(m_desc.at("highlight_by_angle")).x + m_imgui->scaled(1.5f);
const float reset_button_slider_left = m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2;
const float on_overhangs_only_width = m_imgui->calc_text_size(m_desc["on_overhangs_only"]).x + m_imgui->scaled(1.5f);
const float remove_btn_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.5f);
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.5f);
const float gap_area_txt_width = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f);
const float smart_fill_angle_txt_width = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
const float buttons_width = remove_btn_width + filter_btn_width + m_imgui->scaled(1.5f);
const float buttons_width = filter_btn_width + m_imgui->scaled(1.5f);
const float empty_button_width = m_imgui->calc_button_size("").x;
const float tips_width = m_imgui->calc_text_size(_L("Auto support threshold angle: ") + " 90 ").x + m_imgui->scaled(1.5f);
@@ -358,12 +360,27 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##gap_area_input", &TriangleSelectorPatch::gap_area, 0.05f, 0.0f, 0.0f, "%.2f");
// Apply gap fill button
if (m_imgui->button(m_desc.at("perform"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction);
for (int i = 0; i < m_triangle_selectors.size(); i++) {
TriangleSelectorPatch* ts_mm = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
ts_mm->update_selector_triangles();
ts_mm->request_update_render_data(true);
}
update_model_object();
m_parent.set_as_dirty();
}
}
m_imgui->bbl_checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only);
if (ImGui::IsItemHovered())
m_imgui->tooltip(format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]),
max_tooltip_width);
if (m_current_tool != ImGui::GapFillIcon) {
m_imgui->bbl_checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only);
if (ImGui::IsItemHovered())
m_imgui->tooltip(format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]),
max_tooltip_width);
}
ImGui::Separator();
@@ -427,33 +444,12 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
render_tooltip_button(x, y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
// Perform button is for gap fill
if (m_current_tool == ImGui::GapFillIcon) {
if (m_imgui->button(m_desc.at("perform"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction);
for (int i = 0; i < m_triangle_selectors.size(); i++) {
TriangleSelectorPatch* ts_mm = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
ts_mm->update_selector_triangles();
ts_mm->request_update_render_data(true);
}
update_model_object();
m_parent.set_as_dirty();
}
}
ImGui::SameLine();
m_imgui->disabled_begin(m_c->selection_info()->model_object()->is_fdm_support_painted() == false);
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction);
ModelObject * mo = m_c->selection_info()->model_object();
@@ -468,11 +464,18 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
update_model_object();
m_parent.set_as_dirty();
}
ImGui::PopStyleVar(2);
m_imgui->disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
GizmoImguiEnd();
// BBS
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
ImGuiWrapper::pop_toolbar_style();
}

View File

@@ -37,7 +37,7 @@ bool GLGizmoFuzzySkin::on_init()
const wxString shift = GUI::shortkey_shift_prefix();
m_desc["reset_direction"] = _L("Reset direction");
m_desc["remove_all"] = _L("Erase all painting");
m_desc["remove_all"] = _L("Erase all");
m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere");
m_desc["pointer"] = _L("Triangles");
@@ -137,6 +137,8 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
// BBS
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
@@ -151,7 +153,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float buttons_width = m_imgui->scaled(0.5f);
const float minimal_slider_width = m_imgui->scaled(4.f);
@@ -176,7 +177,6 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
const float empty_button_width = m_imgui->calc_button_size("").x;
window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width);
window_width = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer);
window_width = std::max(window_width, tool_type_radio_left + tool_type_radio_brush + tool_type_radio_smart_fill);
window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f));
@@ -299,14 +299,10 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
render_tooltip_button(x, y);
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
m_imgui->disabled_begin(mo->is_fuzzy_skin_painted() == false);
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset selection"), UndoRedo::SnapshotType::GizmoAction);
int idx = -1;
@@ -320,6 +316,13 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
update_model_object();
m_parent.set_as_dirty();
}
m_imgui->disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
const DynamicPrintConfig &glb_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
const bool has_object_fuzzy_override = obj_cfg.option("fuzzy_skin");
@@ -329,7 +332,7 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
float font_size = ImGui::GetFontSize();
auto link_text = [&]() {
ImColor HyperColor = ImGuiWrapper::COL_ORCA;
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING()));
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_WARNING);
float parent_width = ImGui::GetContentRegionAvail().x;
m_imgui->text_wrapped(_L("Warning: Fuzzy skin is disabled, painted fuzzy skin will not take effect!"), parent_width);
ImGui::PopStyleColor();
@@ -350,10 +353,11 @@ void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_lim
mo->config.assign_config(new_conf);
}
};
ImGui::Separator();
link_text();
}
ImGui::PopStyleVar(2);
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
GizmoImguiEnd();
// BBS

View File

@@ -1917,18 +1917,6 @@ void GLGizmoMeasure::show_selection_ui()
if (m_show_reset_first_tip) {
m_imgui->text(_L("Feature 1 has been reset, \nfeature 2 has been feature 1"));
}
if (m_selected_wrong_feature_waring_tip) {
if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) {
if (m_assembly_mode == AssemblyMode::FACE_FACE) {
m_imgui->warning_text(_L("Warning: please select Plane's feature."));
} else if (m_assembly_mode == AssemblyMode::POINT_POINT) {
m_imgui->warning_text(_L("Warning: please select Point's or Circle's feature."));
}
}
}
if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && m_hit_different_volumes.size() == 1) {
m_imgui->warning_text(_L("Warning: please select two different meshes."));
}
}
void GLGizmoMeasure::show_distance_xyz_ui()
@@ -2175,10 +2163,21 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
ImGui::Separator();
show_distance_xyz_ui();
ImGui::Separator();
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({ _L("Done") });
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) {
// the dialog may have changed its size, ask for an extra frame to render it properly
last_feature = m_curr_feature;

View File

@@ -103,7 +103,7 @@ bool GLGizmoMmuSegmentation::on_init()
m_desc["edge_detection"] = _L("Edge detection");
m_desc["gap_area"] = _L("Gap area");
m_desc["perform"] = _L("Perform");
m_desc["remove_all"] = _L("Erase all painting");
m_desc["remove_all"] = _L("Erase all");
m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere");
m_desc["pointer"] = _L("Triangles");
@@ -362,6 +362,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
// BBS
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
@@ -374,10 +377,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
const float gap_area_slider_left = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f) + space_size;
const float height_range_slider_left = m_imgui->calc_text_size(m_desc.at("height_range")).x + m_imgui->scaled(2.f);
const float remove_btn_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.f);
const float remap_btn_width = m_imgui->calc_text_size(m_desc.at("perform_remap")).x + m_imgui->scaled(1.f);
const float buttons_width = remove_btn_width + filter_btn_width + remap_btn_width + m_imgui->scaled(2.f);
const float buttons_width = filter_btn_width + remap_btn_width + m_imgui->scaled(2.f);
const float minimal_slider_width = m_imgui->scaled(4.f);
const float color_button_width = m_imgui->calc_text_size(std::string_view{""}).x + m_imgui->scaled(1.75f);
@@ -566,8 +568,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_cursor_type = TriangleSelector::CursorType::POINTER;
m_tool_type = ToolType::BUCKET_FILL;
m_imgui->bbl_checkbox(m_desc["edge_detection"], m_detect_geometry_edge);
if (m_detect_geometry_edge) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["smart_fill_angle"]);
@@ -587,6 +587,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
// set to negative value to disable edge detection
m_smart_fill_angle = -1.f;
}
m_imgui->bbl_checkbox(m_desc["edge_detection"], m_detect_geometry_edge);
}
else if (m_current_tool == ImGui::HeightRangeIcon) {
m_tool_type = ToolType::BRUSH;
@@ -613,6 +615,19 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##gap_area_input", &TriangleSelectorPatch::gap_area, 0.05f, 0.0f, 0.0f, "%.2f");
// Apply Gap fill button
if (m_imgui->button(m_desc.at("perform"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Gap fill", UndoRedo::SnapshotType::GizmoAction);
for (int i = 0; i < m_triangle_selectors.size(); i++) {
TriangleSelectorPatch* ts_mm = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
ts_mm->update_selector_triangles();
ts_mm->request_update_render_data(true);
}
update_model_object();
m_parent.set_as_dirty();
}
}
ImGui::Separator();
@@ -639,30 +654,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
render_tooltip_button(x, y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
if (m_current_tool == ImGui::GapFillIcon) {
if (m_imgui->button(m_desc.at("perform"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Gap fill", UndoRedo::SnapshotType::GizmoAction);
for (int i = 0; i < m_triangle_selectors.size(); i++) {
TriangleSelectorPatch* ts_mm = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
ts_mm->update_selector_triangles();
ts_mm->request_update_render_data(true);
}
update_model_object();
m_parent.set_as_dirty();
}
ImGui::SameLine();
}
m_imgui->disabled_begin(m_c->selection_info()->model_object()->is_mm_painted() == false);
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction);
ModelObject * mo = m_c->selection_info()->model_object();
@@ -677,7 +672,15 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
update_model_object();
m_parent.set_as_dirty();
}
ImGui::PopStyleVar(2);
m_imgui->disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
GizmoImguiEnd();
// BBS

View File

@@ -41,7 +41,7 @@ bool GLGizmoSeam::on_init()
m_desc["enforce"] = _L("Enforce seam");
m_desc["block"] = _L("Block seam");
m_desc["remove"] = _L("Erase");
m_desc["remove_all"] = _L("Erase all painting");
m_desc["remove_all"] = _L("Erase all");
m_desc["circle"] = _L("Circle");
m_desc["sphere"] = _L("Sphere");
@@ -140,6 +140,8 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
wchar_t old_tool = m_current_tool;
//BBS
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
@@ -256,14 +258,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
GLGizmoUtils::render_tooltip_button(m_imgui, m_parent, m_shortcuts, x, y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
m_imgui->disabled_begin(m_c->selection_info()->model_object()->is_seam_painted() == false);
if (m_imgui->button(m_desc.at("remove_all"))) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction);
ModelObject *mo = m_c->selection_info()->model_object();
@@ -278,7 +276,15 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
update_model_object();
m_parent.set_as_dirty();
}
ImGui::PopStyleVar(2);
m_imgui->disabled_end();
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({_L("Done")});
if (m_imgui->button(_L("Done"))) {
m_parent.reset_all_gizmos();
}
ImGui::PopStyleVar(1); // ImGuiStyleVar_FramePadding
GizmoImguiEnd();
//BBS

View File

@@ -26,6 +26,7 @@
- [Confirm], [Cancel], [Done], ... are buttons that close the Tool Dialog
- [Reset], [Button1], ... are buttons that do not!
- Non-consequential buttons like [Cancel] and [Done] are always the right-most buttons
- [Confirm] buttons should use the orca_button_style to differentiate them from other buttons
- Multiple warnings can show, but should only have one ImGui::Separator above
- If no warnings is shown, dont render the ImGui::Separator
@@ -33,43 +34,101 @@
namespace Slic3r::GUI::GLGizmoUtils {
void render_tooltip_button(
ImGuiWrapper* imgui_wrapper, const GLCanvas3D& canvas, const std::vector<std::pair<wxString, wxString>>& shortcuts, float x, float y)
{
float caption_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
float caption_x_max = 0.f;
for (const auto& item : shortcuts) {
caption_x_max = std::max(caption_x_max, imgui_wrapper->calc_text_size(item.first).x);
}
caption_x_max += imgui_wrapper->calc_text_size(": "sv).x + 35.f;
void render_tooltip_button(
ImGuiWrapper* imgui_wrapper, const GLCanvas3D& canvas, const std::vector<std::pair<wxString, wxString>>& shortcuts, float x, float y)
{
float caption_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
float caption_x_max = 0.f;
for (const auto& item : shortcuts) {
caption_x_max = std::max(caption_x_max, imgui_wrapper->calc_text_size(item.first).x);
}
caption_x_max += imgui_wrapper->calc_text_size(": "sv).x + 35.f;
auto& gizmos_manager = canvas.get_gizmos_manager();
ImTextureID normal_id = gizmos_manager.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
ImTextureID hover_id = gizmos_manager.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
auto& gizmos_manager = canvas.get_gizmos_manager();
ImTextureID normal_id = gizmos_manager.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
ImTextureID hover_id = gizmos_manager.get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
float scale = canvas.get_scale();
float scale = canvas.get_scale();
#ifdef WIN32
int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
scale *= (float) dpi / (float) DPI_DEFAULT;
int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
scale *= (float)dpi / (float)DPI_DEFAULT;
#endif
ImVec2 button_size = ImVec2(25 * scale, 25 * scale);
ImVec2 button_size = ImVec2(25 * scale, 25 * scale);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, 0});
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 });
ImGui::ImageButton3(normal_id, hover_id, button_size);
ImGui::ImageButton3(normal_id, hover_id, button_size);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip2(ImVec2(x, caption_y));
for (const auto& item : shortcuts) {
imgui_wrapper->text_colored(ImGuiWrapper::COL_ACTIVE, item.first + ": ");
ImGui::SameLine(caption_x_max);
imgui_wrapper->text_colored(ImGuiWrapper::COL_WINDOW_BG, item.second);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip2(ImVec2(x, caption_y));
for (const auto& item : shortcuts) {
imgui_wrapper->text_colored(ImGuiWrapper::COL_ACTIVE, item.first + ": ");
ImGui::SameLine(caption_x_max);
imgui_wrapper->text_colored(ImGuiWrapper::COL_WINDOW_BG, item.second);
}
ImGui::EndTooltip();
}
ImGui::EndTooltip();
ImGui::PopStyleVar(2);
}
void begin_right_aligned_buttons(const std::vector<wxString>& labels)
{
float total_width = 0.0f;
ImGuiStyle& style = ImGui::GetStyle();
float spacing = style.ItemSpacing.x;
float padding = style.FramePadding.x * 2.0f;
// Calculate width
for (size_t i = 0; i < labels.size(); ++i) {
total_width += ImGuiWrapper::calc_text_size(labels[i]).x + padding;
if (i < labels.size() - 1)
total_width += spacing;
}
float avail = ImGui::GetContentRegionAvail().x;
// Handle Overlap: If the total width of the buttons exceeds available space, move to a new line
if (total_width > avail) {
ImGui::NewLine();
avail = ImGui::GetContentRegionAvail().x; // Reset to full window width
}
float posX = ImGui::GetCursorPosX() + std::max(0.0f, avail - total_width);
ImGui::SetCursorPosX(posX);
}
void push_orca_button_style()
{
ImVec4 base_orca = ImGuiWrapper::COL_ORCA;
float h, s, v;
ImGui::ColorConvertRGBtoHSV(base_orca.x, base_orca.y, base_orca.z, h, s, v);
ImVec4 hover, active;
// Lighter variant for Hover (Increase Value by ~12%)
ImGui::ColorConvertHSVtoRGB(h, s, std::min(v + 0.12f, 1.0f), hover.x, hover.y, hover.z);
hover.w = base_orca.w;
// Darker variant for Active (Decrease Value by ~12%)
ImGui::ColorConvertHSVtoRGB(h, s, std::max(v - 0.12f, 0.0f), active.x, active.y, active.z);
active.w = base_orca.w;
ImGui::PushStyleColor(ImGuiCol_Button, base_orca);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, hover);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, active);
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_WINDOW_BG);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
}
void pop_orca_button_style()
{
ImGui::PopStyleVar(1);
ImGui::PopStyleColor(4);
}
ImGui::PopStyleVar(2);
}
} // namespace Slic3r::GUI::GLGizmoUtils

View File

@@ -15,9 +15,16 @@ class GLCanvas3D;
namespace GLGizmoUtils {
// Renders a tooltip button using the provided shortcuts
void render_tooltip_button(
ImGuiWrapper* imgui_wrapper, const GLCanvas3D& canvas, const std::vector<std::pair<wxString, wxString>>& shortcuts, float x, float y);
// Renders a tooltip button using the provided shortcuts
void render_tooltip_button(
ImGuiWrapper* imgui_wrapper, const GLCanvas3D& canvas, const std::vector<std::pair<wxString, wxString>>& shortcuts, float x, float y);
// Sets up ImGui to render buttons that are right-aligned within the current window, using the provided labels to calculate spacing.
void begin_right_aligned_buttons(const std::vector<wxString>& labels);
void push_orca_button_style();
void pop_orca_button_style();
} // namespace GLGizmoUtils
} // namespace Slic3r::GUI

View File

@@ -62,16 +62,21 @@ GizmoObjectManipulation::GizmoObjectManipulation(GLCanvas3D& glcanvas)
m_shortcuts_move = {
{alt + _L("Left mouse button"), _L("Part selection")},
{shift + _L("Left mouse button"), _L("Fixed step drag")}
{shift + _L("Left mouse button"), _L("Fixed step drag")},
{_L("Context Menu"), _L("Toggle Auto-Drop")}
};
m_shortcuts_rotate = {
{alt + _L("Left mouse button"), _L("Part selection")}};
{alt + _L("Left mouse button"), _L("Part selection")},
{_L("Context Menu"), _L("Toggle Auto-Drop")}
};
m_shortcuts_scale = {
{alt + _L("Left mouse button"), _L("Part selection")},
{shift + _L("Left mouse button"), _L("Fixed step drag")},
{ctrl + _L("Left mouse button"), _L("Single sided scaling")}};
{ctrl + _L("Left mouse button"), _L("Single sided scaling")},
{_L("Context Menu"), _L("Toggle Auto-Drop")}
};
}
void GizmoObjectManipulation::UpdateAndShow(const bool show)
@@ -860,13 +865,25 @@ void GizmoObjectManipulation::do_render_move_window(ImGuiWrapper *imgui_wrapper,
}
}
if (!focued_on_text) m_glcanvas.handle_sidebar_focus_event("", false);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
float f_scale = m_glcanvas.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GLGizmoUtils::render_tooltip_button(imgui_wrapper, m_glcanvas, m_shortcuts_move, x, y);
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({ _L("Done") });
if (imgui_wrapper->button(_L("Done"))) {
m_glcanvas.reset_all_gizmos();
}
m_last_active_item = current_active_id;
last_move_input_window_width = ImGui::GetWindowWidth();
imgui_wrapper->end();
ImGui::PopStyleVar(1);
ImGui::PopStyleVar(2);
ImGuiWrapper::pop_toolbar_style();
}
@@ -1052,14 +1069,26 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe
if (!focued_on_text && !absolute_focued_on_text)
m_glcanvas.handle_sidebar_focus_event("", false);
ImGui::Spacing(); // needed after Text
ImGui::Separator();
ImGui::Spacing();
float f_scale = m_glcanvas.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GLGizmoUtils::render_tooltip_button(imgui_wrapper, m_glcanvas, m_shortcuts_rotate, x, y);
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({ _L("Done") });
if (imgui_wrapper->button(_L("Done"))) {
m_glcanvas.reset_all_gizmos();
}
m_last_active_item = current_active_id;
last_rotate_input_window_width = ImGui::GetWindowWidth();
imgui_wrapper->end();
// BBS
ImGui::PopStyleVar(1);
ImGui::PopStyleVar(2);
ImGuiWrapper::pop_toolbar_style();
}
@@ -1214,7 +1243,7 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w
if (display_size.x() > 0 && display_size.y() > 0 && display_size.z() > 0) {
m_buffered_size = display_size;
}
ImGui::Separator();
ImGui::AlignTextToFramePadding();
bool is_avoid_one_update{false};
if (combox_changed) {
@@ -1286,15 +1315,26 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w
}
if (!focued_on_text)
m_glcanvas.handle_sidebar_focus_event("", false);
ImGui::Separator();
float f_scale = m_glcanvas.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
GLGizmoUtils::render_tooltip_button(imgui_wrapper, m_glcanvas, m_shortcuts_scale, x, y);
ImGui::SameLine();
GLGizmoUtils::begin_right_aligned_buttons({ _L("Done") });
if (imgui_wrapper->button(_L("Done"))) {
m_glcanvas.reset_all_gizmos();
}
m_last_active_item = current_active_id;
last_scale_input_window_width = ImGui::GetWindowWidth();
imgui_wrapper->end();
//BBS
ImGui::PopStyleVar(1);
ImGuiWrapper::pop_toolbar_style();
}

View File

@@ -185,6 +185,7 @@ const ImVec4 ImGuiWrapper::COL_TOOLBAR_BG = { 250 / 255.f, 250 / 255.f, 2
const ImVec4 ImGuiWrapper::COL_TOOLBAR_BG_DARK = { 57 / 255.f, 60 / 255.f, 66 / 255.f, 1.f }; // ORCA color matches with toolbar_background_dark.png
const ImVec4 ImGuiWrapper::COL_ORCA = to_ImVec4(ColorRGBA::ORCA());
const ImVec4 ImGuiWrapper::COL_MODIFIED = { 253.f / 255.f, 111.f / 255.f, 40.f / 255.f, 1}; // ORCA same color with m_color_label_modified
const ImVec4 ImGuiWrapper::COL_WARNING = to_ImVec4(ColorRGB::WARNING());
int ImGuiWrapper::TOOLBAR_WINDOW_FLAGS = ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_NoMove
@@ -871,8 +872,8 @@ bool ImGuiWrapper::button(const wxString &label, const wxString& tooltip)
const bool ret = ImGui::Button(label_utf8.c_str());
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
auto tooltip_utf8 = into_u8(tooltip);
ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr);
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
this->tooltip(tooltip, max_tooltip_width);
}
return ret;
@@ -884,8 +885,8 @@ bool ImGuiWrapper::bbl_button(const wxString &label, const wxString& tooltip)
const bool ret = ImGui::BBLButton(label_utf8.c_str());
if (!tooltip.IsEmpty() && ImGui::IsItemHovered()) {
auto tooltip_utf8 = into_u8(tooltip);
ImGui::SetTooltip(tooltip_utf8.c_str(), nullptr);
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
this->tooltip(tooltip, max_tooltip_width);
}
return ret;
@@ -1055,7 +1056,7 @@ void ImGuiWrapper::text(const wxString &label)
void ImGuiWrapper::warning_text(const char *label)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING()));
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::COL_WARNING);
this->text(label);
ImGui::PopStyleColor();
}

View File

@@ -349,6 +349,7 @@ public:
static const ImVec4 COL_SEPARATOR_DARK;
static const ImVec4 COL_ORCA;
static const ImVec4 COL_MODIFIED;
static const ImVec4 COL_WARNING;
//BBS
static void on_change_color_mode(bool is_dark);