Merge branch 'master' into fs_emboss
This commit is contained in:
commit
ce2e0e7978
@ -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);
|
||||
}
|
||||
|
||||
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]; }, [](){});
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ private:
|
||||
// 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::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.
|
||||
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);
|
||||
|
||||
|
@ -1805,7 +1805,7 @@ Polygons slice_mesh(
|
||||
{
|
||||
bool trafo_identity = is_identity(params.trafo);
|
||||
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.
|
||||
|
@ -1416,6 +1416,15 @@ void ColourPicker::sys_color_changed()
|
||||
#endif
|
||||
}
|
||||
|
||||
PointCtrl::~PointCtrl()
|
||||
{
|
||||
if (sizer) {
|
||||
sizer->Clear();
|
||||
delete sizer;
|
||||
sizer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PointCtrl::BUILD()
|
||||
{
|
||||
auto temp = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
@ -425,7 +425,7 @@ class PointCtrl : public Field {
|
||||
public:
|
||||
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() {}
|
||||
~PointCtrl();
|
||||
|
||||
wxSizer* sizer{ nullptr };
|
||||
wxTextCtrl* x_textctrl{ nullptr };
|
||||
|
@ -5660,10 +5660,10 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
|
||||
if (wxGetApp().plater()->is_preview_shown())
|
||||
return;
|
||||
|
||||
float scale = wxGetApp().toolbar_icon_scale();
|
||||
Size cnv_size = get_canvas_size();
|
||||
const float scale = wxGetApp().toolbar_icon_scale();
|
||||
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
|
||||
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_undoredo_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
|
||||
m_main_toolbar.set_icons_size(size);
|
||||
m_undoredo_toolbar.set_icons_size(size);
|
||||
collapse_toolbar.set_icons_size(size);
|
||||
#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();
|
||||
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
|
||||
// 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.
|
||||
// See: https://github.com/prusa3d/PrusaSlicer/issues/8563
|
||||
// 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
|
||||
|
||||
// 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
|
||||
float new_scale = std::min(new_h_scale, new_v_scale);
|
||||
@ -5708,24 +5708,12 @@ void GLCanvas3D::_render_overlays()
|
||||
{
|
||||
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();
|
||||
|
||||
_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_undoredo_toolbar();
|
||||
_render_collapse_toolbar();
|
||||
|
@ -1144,11 +1144,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
|
||||
void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data)
|
||||
{
|
||||
CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors;
|
||||
std::pair<Vec3d, Vec3d> pos_and_normal;
|
||||
Vec3d pos;
|
||||
Vec3d pos_world;
|
||||
|
||||
if (unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos_and_normal, pos_world)) {
|
||||
connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first;
|
||||
if (unproject_on_cut_plane(data.mouse_pos.cast<double>(), pos, pos_world)) {
|
||||
connectors[m_hover_id - m_connectors_group_id].pos = pos;
|
||||
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
|
||||
// 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 ModelObject* mo = m_c->selection_info()->model_object();
|
||||
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();
|
||||
|
||||
int mesh_id = -1;
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
++mesh_id;
|
||||
if (!mv->is_model_part())
|
||||
continue;
|
||||
Vec3f normal;
|
||||
Vec3f hit;
|
||||
bool clipping_plane_was_hit = false;
|
||||
// Calculate intersection with the clipping plane.
|
||||
const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(true);
|
||||
Vec3d point;
|
||||
Vec3d direction;
|
||||
Vec3d hit;
|
||||
MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, point, direction);
|
||||
Vec3d normal = -cp->get_normal().cast<double>();
|
||||
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);
|
||||
const Transform3d volume_trafo = mv->get_transformation().get_matrix();
|
||||
if (! m_c->object_clipper()->is_projection_inside_cut(hit))
|
||||
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
|
||||
Vec3d hit_d = hit.cast<double>();
|
||||
Vec3d hit_d = hit;
|
||||
hit_d -= mi->get_offset();
|
||||
hit_d[Z] -= sla_shift;
|
||||
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit_d, normal.cast<double>());
|
||||
pos_world = hit.cast<double>();
|
||||
pos = hit_d;
|
||||
pos_world = hit;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::clear_selection()
|
||||
@ -2139,17 +2136,13 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p
|
||||
if (!m_connectors_editing)
|
||||
return false;
|
||||
|
||||
std::pair<Vec3d, Vec3d> pos_and_normal;
|
||||
Vec3d pos;
|
||||
Vec3d pos_world;
|
||||
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos_and_normal, 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;
|
||||
|
||||
if (unproject_on_cut_plane(mouse_position.cast<double>(), pos, pos_world)) {
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction);
|
||||
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_tolerance, m_connector_depth_ratio_tolerance,
|
||||
CutConnectorAttributes( CutConnectorType(m_connector_type),
|
||||
@ -2247,9 +2240,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi
|
||||
if (!m_connectors_editing) {
|
||||
if (0 && action == SLAGizmoEventType::LeftDown) {
|
||||
// disable / enable current contour
|
||||
std::pair<Vec3d, Vec3d> pos_and_normal;
|
||||
Vec3d pos;
|
||||
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
|
||||
// toggle the respective contour as disabled.
|
||||
m_c->object_clipper()->pass_mouse_click(pos_world);
|
||||
|
@ -155,7 +155,7 @@ public:
|
||||
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
|
||||
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 is_in_editing_mode() const override { return m_connectors_editing; }
|
||||
|
@ -22,7 +22,7 @@ namespace GUI {
|
||||
|
||||
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_fill_cut = fill_cut;
|
||||
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,
|
||||
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 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;
|
||||
}
|
||||
|
||||
if (i==hits.size()) {
|
||||
// All hits are clipped.
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -423,24 +403,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
||||
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
|
||||
{
|
||||
|
@ -153,16 +153,12 @@ public:
|
||||
const Vec2d& mouse_pos,
|
||||
const Transform3d& trafo, // how to get the mesh into world coords
|
||||
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
|
||||
const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active)
|
||||
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?
|
||||
size_t* facet_idx = nullptr // index of the facet hit
|
||||
) 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;
|
||||
|
||||
// Given a vector of points in woorld coordinates, this returns vector
|
||||
|
@ -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
|
||||
{
|
||||
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;
|
||||
|
||||
if (!custom_ctrl && m_use_custom_ctrl) {
|
||||
@ -263,15 +281,13 @@ void OptionsGroup::activate_line(Line& line)
|
||||
return;
|
||||
}
|
||||
|
||||
auto grid_sizer = m_grid_sizer;
|
||||
|
||||
if (custom_ctrl)
|
||||
m_use_custom_ctrl_as_parent = true;
|
||||
|
||||
// if we have an extra column, build it
|
||||
if (extra_column) {
|
||||
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
|
||||
@ -298,12 +314,12 @@ void OptionsGroup::activate_line(Line& line)
|
||||
label->Wrap(label_width * wxGetApp().em_unit()); // avoid a Linux/GTK bug
|
||||
}
|
||||
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()) {
|
||||
// If we're here, we have some widget near the label
|
||||
// so we need a horizontal sizer to arrange these things
|
||||
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);
|
||||
}
|
||||
if (label != nullptr && line.label_tooltip != "")
|
||||
@ -317,15 +333,19 @@ void OptionsGroup::activate_line(Line& line)
|
||||
if (custom_ctrl)
|
||||
line.widget_sizer = wgt;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
if (!custom_ctrl)
|
||||
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
|
||||
wxBoxSizer* h_sizer{ nullptr };
|
||||
if (!custom_ctrl) {
|
||||
// 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 (option_set.size() == 1 && option_set.front().opt.sidetext.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 (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);
|
||||
if (is_sizer_field(field))
|
||||
sizer->Add(field->getSizer(), 1, (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), 0);
|
||||
} else
|
||||
delete sizer;
|
||||
h_sizer->Add(field->getSizer(), 1, (option.opt.full_width ? int(wxEXPAND) : int(wxALIGN_CENTER_VERTICAL)), 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool sizer_is_used = false;
|
||||
bool is_multioption_line = option_set.size() > 1;
|
||||
for (auto opt : option_set) {
|
||||
for (const Option& opt : option_set) {
|
||||
// add field
|
||||
auto& field = build_field(opt);
|
||||
|
||||
if (!custom_ctrl) {
|
||||
ConfigOptionDef option = opt.opt;
|
||||
wxSizer* sizer_tmp = sizer;
|
||||
// add label if any
|
||||
if ((is_multioption_line || line.label.IsEmpty()) && !option.label.empty() && !custom_ctrl) {
|
||||
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
||||
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
|
||||
wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ?
|
||||
_CTX(option.label, "Layers") :
|
||||
_(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);
|
||||
label->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
label->SetFont(wxGetApp().normal_font());
|
||||
sizer_tmp->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
sizer_is_used = true;
|
||||
h_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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) ?
|
||||
v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
|
||||
v_sizer->Add(field->getWindow(), 0, wxEXPAND);
|
||||
@ -377,8 +392,8 @@ void OptionsGroup::activate_line(Line& line)
|
||||
}
|
||||
|
||||
is_sizer_field(field) ?
|
||||
sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
|
||||
sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
h_sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
|
||||
h_sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
// add sidetext if any
|
||||
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);
|
||||
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
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
|
||||
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())
|
||||
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
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
line.extra_widget_sizer = extra_widget(this->ctrl_parent());
|
||||
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
|
||||
@ -484,20 +496,8 @@ void OptionsGroup::clear(bool destroy_custom_ctrl)
|
||||
m_grid_sizer = nullptr;
|
||||
sizer = nullptr;
|
||||
|
||||
for (Line& line : m_lines) {
|
||||
if (line.near_label_widget_win)
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (Line& line : m_lines)
|
||||
line.clear();
|
||||
|
||||
if (custom_ctrl) {
|
||||
for (auto const &item : m_fields) {
|
||||
|
@ -86,6 +86,7 @@ public:
|
||||
|
||||
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; }
|
||||
void clear();
|
||||
|
||||
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
|
||||
const std::vector<Option>& get_options() const { return m_options; }
|
||||
|
@ -1784,6 +1784,8 @@ void TabPrint::clear_pages()
|
||||
m_recommended_thin_wall_thickness_description_line = nullptr;
|
||||
m_top_bottom_shell_thickness_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)
|
||||
|
Loading…
Reference in New Issue
Block a user