Merge branch 'master' into fs_emboss

This commit is contained in:
Filip Sykala - NTB T15p 2022-12-08 08:54:26 +01:00
commit ce2e0e7978
13 changed files with 132 additions and 180 deletions

View File

@ -622,7 +622,7 @@ std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::funct
return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback); return its_face_edge_ids_impl(its, [](const uint32_t){ return true; }, throw_on_cancel_callback);
} }
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask) std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask)
{ {
return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){}); return its_face_edge_ids_impl(its, [&face_mask](const uint32_t idx){ return face_mask[idx]; }, [](){});
} }

View File

@ -188,7 +188,7 @@ private:
// Used for chaining slice lines into polygons. // Used for chaining slice lines into polygons.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<bool> &face_mask); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, const std::vector<char> &face_mask);
// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices. // Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr); std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);

View File

@ -1805,7 +1805,7 @@ Polygons slice_mesh(
{ {
bool trafo_identity = is_identity(params.trafo); bool trafo_identity = is_identity(params.trafo);
Transform3f tf; Transform3f tf;
std::vector<bool> face_mask(mesh.indices.size(), false); std::vector<char> face_mask(mesh.indices.size(), 0);
{ {
// 1) Mark vertices as below or above the slicing plane. // 1) Mark vertices as below or above the slicing plane.

View File

@ -1416,6 +1416,15 @@ void ColourPicker::sys_color_changed()
#endif #endif
} }
PointCtrl::~PointCtrl()
{
if (sizer) {
sizer->Clear();
delete sizer;
sizer = nullptr;
}
}
void PointCtrl::BUILD() void PointCtrl::BUILD()
{ {
auto temp = new wxBoxSizer(wxHORIZONTAL); auto temp = new wxBoxSizer(wxHORIZONTAL);

View File

@ -425,7 +425,7 @@ class PointCtrl : public Field {
public: public:
PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~PointCtrl() {} ~PointCtrl();
wxSizer* sizer{ nullptr }; wxSizer* sizer{ nullptr };
wxTextCtrl* x_textctrl{ nullptr }; wxTextCtrl* x_textctrl{ nullptr };

View File

@ -5660,10 +5660,10 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
if (wxGetApp().plater()->is_preview_shown()) if (wxGetApp().plater()->is_preview_shown())
return; return;
float scale = wxGetApp().toolbar_icon_scale(); const float scale = wxGetApp().toolbar_icon_scale();
Size cnv_size = get_canvas_size(); const Size cnv_size = get_canvas_size();
float size = GLToolbar::Default_Icons_Size * scale; int size = int(GLToolbar::Default_Icons_Size * scale);
// Set current size for all top toolbars. It will be used for next calculations // Set current size for all top toolbars. It will be used for next calculations
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
@ -5672,28 +5672,28 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
m_main_toolbar.set_scale(sc); m_main_toolbar.set_scale(sc);
m_undoredo_toolbar.set_scale(sc); m_undoredo_toolbar.set_scale(sc);
collapse_toolbar.set_scale(sc); collapse_toolbar.set_scale(sc);
size *= m_retina_helper->get_scale_factor(); size *= int(m_retina_helper->get_scale_factor());
#else #else
m_main_toolbar.set_icons_size(size); m_main_toolbar.set_icons_size(size);
m_undoredo_toolbar.set_icons_size(size); m_undoredo_toolbar.set_icons_size(size);
collapse_toolbar.set_icons_size(size); collapse_toolbar.set_icons_size(size);
#endif // ENABLE_RETINA_GL #endif // ENABLE_RETINA_GL
float top_tb_width = m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar.get_width(); const float top_tb_width = m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar.get_width();
int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_undoredo_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt(); int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_undoredo_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt();
float noitems_width = top_tb_width - size * items_cnt; // width of separators and borders in top toolbars const float noitems_width = top_tb_width - float(size) * items_cnt; // width of separators and borders in top toolbars
// calculate scale needed for items in all top toolbars // calculate scale needed for items in all top toolbars
// the std::max() is there because on some Linux dialects/virtual machines this code is called when the canvas has not been properly initialized yet, // the std::max() is there because on some Linux dialects/virtual machines this code is called when the canvas has not been properly initialized yet,
// leading to negative values for the scale. // leading to negative values for the scale.
// See: https://github.com/prusa3d/PrusaSlicer/issues/8563 // See: https://github.com/prusa3d/PrusaSlicer/issues/8563
// https://github.com/supermerill/SuperSlicer/issues/854 // https://github.com/supermerill/SuperSlicer/issues/854
float new_h_scale = std::max((cnv_size.get_width() - noitems_width), 1.0f) / (items_cnt * GLToolbar::Default_Icons_Size); const float new_h_scale = std::max((cnv_size.get_width() - noitems_width), 1.0f) / (items_cnt * GLToolbar::Default_Icons_Size);
items_cnt = m_gizmos.get_selectable_icons_cnt() + 3; // +3 means a place for top and view toolbars and separators in gizmos toolbar items_cnt = m_gizmos.get_selectable_icons_cnt() + 3; // +3 means a place for top and view toolbars and separators in gizmos toolbar
// calculate scale needed for items in the gizmos toolbar // calculate scale needed for items in the gizmos toolbar
float new_v_scale = cnv_size.get_height() / (items_cnt * GLGizmosManager::Default_Icons_Size); const float new_v_scale = cnv_size.get_height() / (items_cnt * GLGizmosManager::Default_Icons_Size);
// set minimum scale as a auto scale for the toolbars // set minimum scale as a auto scale for the toolbars
float new_scale = std::min(new_h_scale, new_v_scale); float new_scale = std::min(new_h_scale, new_v_scale);
@ -5708,24 +5708,12 @@ void GLCanvas3D::_render_overlays()
{ {
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
// to correctly place them
_check_and_update_toolbar_icon_scale(); _check_and_update_toolbar_icon_scale();
_render_gizmos_overlay(); _render_gizmos_overlay();
// main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed
// to correctly place them
#if ENABLE_RETINA_GL
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/);
m_main_toolbar.set_scale(scale);
m_undoredo_toolbar.set_scale(scale);
wxGetApp().plater()->get_collapse_toolbar().set_scale(scale);
#else
const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/));
m_main_toolbar.set_icons_size(size);
m_undoredo_toolbar.set_icons_size(size);
wxGetApp().plater()->get_collapse_toolbar().set_icons_size(size);
#endif // ENABLE_RETINA_GL
_render_main_toolbar(); _render_main_toolbar();
_render_undoredo_toolbar(); _render_undoredo_toolbar();
_render_collapse_toolbar(); _render_collapse_toolbar();

View File

@ -1144,11 +1144,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data) void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data)
{ {
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
std::pair<Vec3d, Vec3d> pos_and_normal; Vec3d pos;
Vec3d pos_world; Vec3d pos_world;
if (unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal, pos_world)) { if (unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos, pos_world)) {
connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; connectors[m_hover_id - m_connectors_group_id].pos = pos;
update_raycasters_for_picking_transform(); update_raycasters_for_picking_transform();
} }
} }
@ -2006,44 +2006,41 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
// Return false if no intersection was found, true otherwise. // Return false if no intersection was found, true otherwise.
bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair<Vec3d, Vec3d>& pos_and_normal, Vec3d& pos_world) bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world)
{ {
const float sla_shift = m_c->selection_info()->get_sla_shift(); const float sla_shift = m_c->selection_info()->get_sla_shift();
const ModelObject* mo = m_c->selection_info()->model_object(); const ModelObject* mo = m_c->selection_info()->model_object();
const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()];
const Transform3d instance_trafo = sla_shift > 0.f ?
translation_transform(sla_shift * Vec3d::UnitZ()) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix();
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
int mesh_id = -1; // Calculate intersection with the clipping plane.
for (const ModelVolume* mv : mo->volumes) { const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(true);
++mesh_id; Vec3d point;
if (!mv->is_model_part()) Vec3d direction;
continue; Vec3d hit;
Vec3f normal; MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, point, direction);
Vec3f hit; Vec3d normal = -cp->get_normal().cast<double>();
bool clipping_plane_was_hit = false; double den = normal.dot(direction);
if (den != 0.) {
double t = (-cp->get_offset() - normal.dot(point))/den;
hit = (point + t * direction);
} else
return false;
// const Transform3d volume_trafo = get_volume_transformation(mv); if (! m_c->object_clipper()->is_projection_inside_cut(hit))
const Transform3d volume_trafo = mv->get_transformation().get_matrix(); return false;
m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo,
camera, hit, normal, m_c->object_clipper()->get_clipping_plane(true),
nullptr, &clipping_plane_was_hit);
if (clipping_plane_was_hit) {
// recalculate hit to object's local position // recalculate hit to object's local position
Vec3d hit_d = hit.cast<double>(); Vec3d hit_d = hit;
hit_d -= mi->get_offset(); hit_d -= mi->get_offset();
hit_d[Z] -= sla_shift; hit_d[Z] -= sla_shift;
// Return both the point and the facet normal. // Return both the point and the facet normal.
pos_and_normal = std::make_pair(hit_d, normal.cast<double>()); pos = hit_d;
pos_world = hit.cast<double>(); pos_world = hit;
return true; return true;
}
}
return false;
} }
void GLGizmoCut3D::clear_selection() void GLGizmoCut3D::clear_selection()
@ -2139,17 +2136,13 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p
if (!m_connectors_editing) if (!m_connectors_editing)
return false; return false;
std::pair<Vec3d, Vec3d> pos_and_normal; Vec3d pos;
Vec3d pos_world; Vec3d pos_world;
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal, pos_world)) { if (unproject_on_cut_plane(mouse_position.cast<double>(), pos, pos_world)) {
// check if pos is out of enabled clipping plane
if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(pos_world))
return true;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction);
unselect_all_connectors(); unselect_all_connectors();
connectors.emplace_back(pos_and_normal.first, m_rotation_m, connectors.emplace_back(pos, m_rotation_m,
m_connector_size * 0.5f, m_connector_depth_ratio, m_connector_size * 0.5f, m_connector_depth_ratio,
m_connector_size_tolerance, m_connector_depth_ratio_tolerance, m_connector_size_tolerance, m_connector_depth_ratio_tolerance,
CutConnectorAttributes( CutConnectorType(m_connector_type), CutConnectorAttributes( CutConnectorType(m_connector_type),
@ -2247,9 +2240,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
if (!m_connectors_editing) { if (!m_connectors_editing) {
if (0 && action == SLAGizmoEventType::LeftDown) { if (0 && action == SLAGizmoEventType::LeftDown) {
// disable / enable current contour // disable / enable current contour
std::pair<Vec3d, Vec3d> pos_and_normal; Vec3d pos;
Vec3d pos_world; Vec3d pos_world;
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal, pos_world)) { if (unproject_on_cut_plane(mouse_position.cast<double>(), pos, pos_world)) {
// Following would inform the clipper about the mouse click, so it can // Following would inform the clipper about the mouse click, so it can
// toggle the respective contour as disabled. // toggle the respective contour as disabled.
m_c->object_clipper()->pass_mouse_click(pos_world); m_c->object_clipper()->pass_mouse_click(pos_world);

View File

@ -155,7 +155,7 @@ public:
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
std::string get_tooltip() const override; std::string get_tooltip() const override;
bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair<Vec3d, Vec3d>& pos_and_normal, Vec3d& pos_world); bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool is_in_editing_mode() const override { return m_connectors_editing; } bool is_in_editing_mode() const override { return m_connectors_editing; }

View File

@ -22,7 +22,7 @@ namespace GUI {
void MeshClipper::set_behaviour(bool fill_cut, double contour_width) void MeshClipper::set_behaviour(bool fill_cut, double contour_width)
{ {
if (fill_cut != m_fill_cut || is_approx(contour_width, m_contour_width)) if (fill_cut != m_fill_cut || ! is_approx(contour_width, m_contour_width))
m_result.reset(); m_result.reset();
m_fill_cut = fill_cut; m_fill_cut = fill_cut;
m_contour_width = contour_width; m_contour_width = contour_width;
@ -365,11 +365,8 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
size_t* facet_idx, bool* was_clipping_plane_hit) const size_t* facet_idx) const
{ {
if (was_clipping_plane_hit)
*was_clipping_plane_hit = false;
Vec3d point; Vec3d point;
Vec3d direction; Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
@ -390,26 +387,9 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
break; break;
} }
if (i==hits.size()) { if (i==hits.size() || (hits.size()-i) % 2 != 0) {
// All hits are clipped. // All hits are either clipped, or there is an odd number of unclipped
return false; // hits - meaning the nearest must be from inside the mesh.
}
if (clipping_plane && (hits.size()-i) % 2 != 0) {
// There is an odd number of unclipped hits - meaning the nearest must be from inside the mesh.
// In that case, calculate intersection with the clipping place.
if (was_clipping_plane_hit) {
direction = direction + point;
point = trafo * point; // transform to world coords
direction = trafo * direction - point;
Vec3d normal = -clipping_plane->get_normal().cast<double>();
double den = normal.dot(direction);
if (den != 0.) {
double t = (-clipping_plane->get_offset() - normal.dot(point))/den;
position = (point + t * direction).cast<float>();
*was_clipping_plane_hit = true;
}
}
return false; return false;
} }
@ -423,24 +403,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
return true; return true;
} }
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& position, Vec3d& normal) const
{
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
if (hits.empty())
return false; // no intersection found
// Now stuff the points in the provided vector and calculate normals if asked about them:
position = hits[0].position();
normal = hits[0].normal();
return true;
}
bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const
{ {

View File

@ -153,16 +153,12 @@ public:
const Vec2d& mouse_pos, const Vec2d& mouse_pos,
const Transform3d& trafo, // how to get the mesh into world coords const Transform3d& trafo, // how to get the mesh into world coords
const Camera& camera, // current camera position const Camera& camera, // current camera position
Vec3f& position, // where to save the positibon of the hit (mesh coords if mesh, world coords if clipping plane) Vec3f& position, // where to save the positibon of the hit (mesh coords)
Vec3f& normal, // normal of the triangle that was hit Vec3f& normal, // normal of the triangle that was hit
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
size_t* facet_idx = nullptr, // index of the facet hit size_t* facet_idx = nullptr // index of the facet hit
bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section?
) const; ) const;
// Given a mouse position, this returns true in case it is on the mesh.
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& position, Vec3d& normal) const;
bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const;
// Given a vector of points in woorld coordinates, this returns vector // Given a vector of points in woorld coordinates, this returns vector

View File

@ -118,6 +118,24 @@ OptionsGroup::OptionsGroup( wxWindow* _parent, const wxString& title,
{ {
} }
void Line::clear()
{
if (near_label_widget_win)
near_label_widget_win = nullptr;
if (widget_sizer) {
widget_sizer->Clear(true);
delete widget_sizer;
widget_sizer = nullptr;
}
if (extra_widget_sizer) {
extra_widget_sizer->Clear(true);
delete extra_widget_sizer;
extra_widget_sizer = nullptr;
}
}
wxWindow* OptionsGroup::ctrl_parent() const wxWindow* OptionsGroup::ctrl_parent() const
{ {
return this->custom_ctrl && m_use_custom_ctrl_as_parent ? static_cast<wxWindow*>(this->custom_ctrl) : (this->stb ? static_cast<wxWindow*>(this->stb) : this->parent()); return this->custom_ctrl && m_use_custom_ctrl_as_parent ? static_cast<wxWindow*>(this->custom_ctrl) : (this->stb ? static_cast<wxWindow*>(this->stb) : this->parent());
@ -231,7 +249,7 @@ void OptionsGroup::activate_line(Line& line)
} }
} }
auto option_set = line.get_options(); const std::vector<Option>& option_set = line.get_options();
bool is_legend_line = option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend; bool is_legend_line = option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend;
if (!custom_ctrl && m_use_custom_ctrl) { if (!custom_ctrl && m_use_custom_ctrl) {
@ -263,15 +281,13 @@ void OptionsGroup::activate_line(Line& line)
return; return;
} }
auto grid_sizer = m_grid_sizer;
if (custom_ctrl) if (custom_ctrl)
m_use_custom_ctrl_as_parent = true; m_use_custom_ctrl_as_parent = true;
// if we have an extra column, build it // if we have an extra column, build it
if (extra_column) { if (extra_column) {
m_extra_column_item_ptrs.push_back(extra_column(this->ctrl_parent(), line)); m_extra_column_item_ptrs.push_back(extra_column(this->ctrl_parent(), line));
grid_sizer->Add(m_extra_column_item_ptrs.back(), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 3); m_grid_sizer->Add(m_extra_column_item_ptrs.back(), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 3);
} }
// Build a label if we have it // Build a label if we have it
@ -298,12 +314,12 @@ void OptionsGroup::activate_line(Line& line)
label->Wrap(label_width * wxGetApp().em_unit()); // avoid a Linux/GTK bug label->Wrap(label_width * wxGetApp().em_unit()); // avoid a Linux/GTK bug
} }
if (!line.near_label_widget) if (!line.near_label_widget)
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, line.label.IsEmpty() ? 0 : 5); m_grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, line.label.IsEmpty() ? 0 : 5);
else if (!line.label.IsEmpty()) { else if (!line.label.IsEmpty()) {
// If we're here, we have some widget near the label // If we're here, we have some widget near the label
// so we need a horizontal sizer to arrange these things // so we need a horizontal sizer to arrange these things
auto sizer = new wxBoxSizer(wxHORIZONTAL); auto sizer = new wxBoxSizer(wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); m_grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5); sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
} }
if (label != nullptr && line.label_tooltip != "") if (label != nullptr && line.label_tooltip != "")
@ -317,15 +333,19 @@ void OptionsGroup::activate_line(Line& line)
if (custom_ctrl) if (custom_ctrl)
line.widget_sizer = wgt; line.widget_sizer = wgt;
else else
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5); m_grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5);
return; return;
} }
// If we're here, we have more than one option or a single option with sidetext // If we're here, we have more than one option or a single option with sidetext
// so we need a horizontal sizer to arrange these things // so we need a horizontal sizer to arrange these things
auto sizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* h_sizer{ nullptr };
if (!custom_ctrl) if (!custom_ctrl) {
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); // but this sizer is currently used just for NON-custom_ctrl cases
h_sizer = new wxBoxSizer(wxHORIZONTAL);
m_grid_sizer->Add(h_sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
}
// If we have a single option with no sidetext just add it directly to the grid sizer // If we have a single option with no sidetext just add it directly to the grid sizer
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
@ -334,23 +354,23 @@ void OptionsGroup::activate_line(Line& line)
if (!custom_ctrl) { if (!custom_ctrl) {
if (is_window_field(field)) if (is_window_field(field))
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, h_sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0,
wxBOTTOM | wxTOP | (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), (wxOSX || !staticbox) ? 0 : 2); wxBOTTOM | wxTOP | (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), (wxOSX || !staticbox) ? 0 : 2);
if (is_sizer_field(field)) if (is_sizer_field(field))
sizer->Add(field->getSizer(), 1, (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), 0); h_sizer->Add(field->getSizer(), 1, (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), 0);
} else }
delete sizer;
return; return;
} }
bool sizer_is_used = false; for (const Option& opt : option_set) {
bool is_multioption_line = option_set.size() > 1; // add field
for (auto opt : option_set) { auto& field = build_field(opt);
if (!custom_ctrl) {
ConfigOptionDef option = opt.opt; ConfigOptionDef option = opt.opt;
wxSizer* sizer_tmp = sizer;
// add label if any // add label if any
if ((is_multioption_line || line.label.IsEmpty()) && !option.label.empty() && !custom_ctrl) { if ((option_set.size() > 1 || line.label.IsEmpty()) && !option.label.empty()) {
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 // To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ?
_CTX(option.label, "Layers") : _CTX(option.label, "Layers") :
_(option.label); _(option.label);
@ -358,18 +378,13 @@ void OptionsGroup::activate_line(Line& line)
wxSize(sublabel_width != -1 ? sublabel_width * wxGetApp().em_unit() : -1, -1), wxALIGN_RIGHT); wxSize(sublabel_width != -1 ? sublabel_width * wxGetApp().em_unit() : -1, -1), wxALIGN_RIGHT);
label->SetBackgroundStyle(wxBG_STYLE_PAINT); label->SetBackgroundStyle(wxBG_STYLE_PAINT);
label->SetFont(wxGetApp().normal_font()); label->SetFont(wxGetApp().normal_font());
sizer_tmp->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0); h_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
sizer_is_used = true;
} }
// add field
const Option& opt_ref = opt;
auto& field = build_field(opt_ref);
if (!custom_ctrl) {
if (option_set.size() == 1 && option_set.front().opt.full_width) if (option_set.size() == 1 && option_set.front().opt.full_width)
{ {
const auto v_sizer = new wxBoxSizer(wxVERTICAL); const auto v_sizer = new wxBoxSizer(wxVERTICAL);
sizer_tmp->Add(v_sizer, 1, wxEXPAND); h_sizer->Add(v_sizer, 1, wxEXPAND);
is_sizer_field(field) ? is_sizer_field(field) ?
v_sizer->Add(field->getSizer(), 0, wxEXPAND) : v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
v_sizer->Add(field->getWindow(), 0, wxEXPAND); v_sizer->Add(field->getWindow(), 0, wxEXPAND);
@ -377,8 +392,8 @@ void OptionsGroup::activate_line(Line& line)
} }
is_sizer_field(field) ? is_sizer_field(field) ?
sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) : h_sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); h_sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
// add sidetext if any // add sidetext if any
if (!option.sidetext.empty() || sidetext_width > 0) { if (!option.sidetext.empty() || sidetext_width > 0) {
@ -386,16 +401,16 @@ void OptionsGroup::activate_line(Line& line)
wxSize(sidetext_width != -1 ? sidetext_width * wxGetApp().em_unit() : -1, -1), wxALIGN_LEFT); wxSize(sidetext_width != -1 ? sidetext_width * wxGetApp().em_unit() : -1, -1), wxALIGN_LEFT);
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT); sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
sidetext->SetFont(wxGetApp().normal_font()); sidetext->SetFont(wxGetApp().normal_font());
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); h_sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
} }
// add side widget if any // add side widget if any
if (opt.side_widget != nullptr) { if (opt.side_widget != nullptr) {
sizer_tmp->Add(opt.side_widget(this->ctrl_parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification h_sizer->Add(opt.side_widget(this->ctrl_parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification
} }
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back()) if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
sizer_tmp->AddSpacer(6); h_sizer->AddSpacer(6);
} }
} }
@ -406,18 +421,15 @@ void OptionsGroup::activate_line(Line& line)
{ {
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT // extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
const auto v_sizer = new wxBoxSizer(wxVERTICAL); const auto v_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND); h_sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND);
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT); v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
return; return;
} }
line.extra_widget_sizer = extra_widget(this->ctrl_parent()); line.extra_widget_sizer = extra_widget(this->ctrl_parent());
if (!custom_ctrl) if (!custom_ctrl)
sizer->Add(line.extra_widget_sizer, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification h_sizer->Add(line.extra_widget_sizer, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
} }
if (custom_ctrl && !sizer_is_used)
delete sizer;
} }
// create all controls for the option group from the m_lines // create all controls for the option group from the m_lines
@ -484,20 +496,8 @@ void OptionsGroup::clear(bool destroy_custom_ctrl)
m_grid_sizer = nullptr; m_grid_sizer = nullptr;
sizer = nullptr; sizer = nullptr;
for (Line& line : m_lines) { for (Line& line : m_lines)
if (line.near_label_widget_win) line.clear();
line.near_label_widget_win = nullptr;
if (line.widget_sizer) {
line.widget_sizer->Clear(true);
line.widget_sizer = nullptr;
}
if (line.extra_widget_sizer) {
line.extra_widget_sizer->Clear(true);
line.extra_widget_sizer = nullptr;
}
}
if (custom_ctrl) { if (custom_ctrl) {
for (auto const &item : m_fields) { for (auto const &item : m_fields) {

View File

@ -86,6 +86,7 @@ public:
bool is_separator() const { return m_is_separator; } bool is_separator() const { return m_is_separator; }
bool has_only_option(const std::string& opt_key) const { return m_options.size() == 1 && m_options[0].opt_id == opt_key; } bool has_only_option(const std::string& opt_key) const { return m_options.size() == 1 && m_options[0].opt_id == opt_key; }
void clear();
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;} const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
const std::vector<Option>& get_options() const { return m_options; } const std::vector<Option>& get_options() const { return m_options; }

View File

@ -1784,6 +1784,8 @@ void TabPrint::clear_pages()
m_recommended_thin_wall_thickness_description_line = nullptr; m_recommended_thin_wall_thickness_description_line = nullptr;
m_top_bottom_shell_thickness_explanation = nullptr; m_top_bottom_shell_thickness_explanation = nullptr;
m_post_process_explanation = nullptr; m_post_process_explanation = nullptr;
m_del_all_substitutions_btn = nullptr;
} }
bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode) bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode)