From c9c407cc2b3982ba4883ed139b7ec30668626d53 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 18 Sep 2019 15:10:36 +0200 Subject: [PATCH 01/23] Rewrote UI for ObjectManipulation without using of OptionsGrope --- src/slic3r/GUI/GUI_ObjectList.cpp | 3 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 346 +++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 10 +- src/slic3r/GUI/wxExtensions.cpp | 2 + 4 files changed, 354 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 51098647f..c67f0d304 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2217,7 +2217,8 @@ void ObjectList::part_selection_changed() wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " "); if (item) { - wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); + // wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); + wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item)); wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id)); } } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2295ac0b6..963ae53ab 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -112,7 +112,295 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo) combo->SetValue(selection); } +static void set_font_and_background_style(wxWindow* win, const wxFont& font) +{ + win->SetFont(font); + win->SetBackgroundStyle(wxBG_STYLE_PAINT); +} +ObjectManipulation::ObjectManipulation(wxWindow* parent) : + OG_Settings(parent, true) +#ifndef __APPLE__ + , m_focused_option("") +#endif // __APPLE__ +{ + const int border = wxOSX ? 0 : 4; + const int em = wxGetApp().em_unit(); + m_main_grid_sizer = new wxFlexGridSizer(2, 3, 3); // "Name/label", "String name / Editors" + m_main_grid_sizer->SetFlexibleDirection(wxBOTH); + + // Add "Name" label with warning icon + auto sizer = new wxBoxSizer(wxHORIZONTAL); + + m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); + if (is_windows10()) + m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent& e) + { + // if object/sub-object has no errors + if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData()) + return; + + wxGetApp().obj_list()->fix_through_netfabb(); + update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list()); + }); + + sizer->Add(m_fix_throught_netfab_bitmap); + + auto name_label = new wxStaticText(m_parent, wxID_ANY, _(L("Name"))+":"); + set_font_and_background_style(name_label, wxGetApp().normal_font()); + name_label->SetToolTip(_(L("Object name"))); + sizer->Add(name_label); + + m_main_grid_sizer->Add(sizer); + + // Add name of the item + const wxSize name_size = wxSize(20 * em, wxDefaultCoord); + m_item_name = new wxStaticText(m_parent, wxID_ANY, "", wxDefaultPosition, name_size, wxST_ELLIPSIZE_MIDDLE); + set_font_and_background_style(m_item_name, wxGetApp().bold_font()); + + m_main_grid_sizer->Add(m_item_name, 0, wxEXPAND); + + // Add labels grid sizer + m_labels_grid_sizer = new wxFlexGridSizer(1, 3, 3); // "Name/label", "String name / Editors" + m_labels_grid_sizer->SetFlexibleDirection(wxBOTH); + + // Add world local combobox + m_word_local_combo = create_word_local_combo(parent); + m_word_local_combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent& evt) { + this->set_world_coordinates(evt.GetSelection() != 1); + }), m_word_local_combo->GetId()); + + // Small trick to correct layouting in different view_mode : + // Show empty string of a same height as a m_word_local_combo, when m_word_local_combo is hidden + sizer = new wxBoxSizer(wxHORIZONTAL); + m_empty_str = new wxStaticText(parent, wxID_ANY, ""); + sizer->Add(m_word_local_combo); + sizer->Add(m_empty_str); + sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); + m_labels_grid_sizer->Add(sizer); + + // Text trick to grid sizer layout: + // Height of labels should be equivalent to the edit boxes + int height = wxTextCtrl(parent, wxID_ANY, "Br").GetBestHeight(-1); + + auto add_label = [this, height](wxStaticText** label, std::string name, wxSizer* resive_sizer = nullptr) + { + *label = new wxStaticText(m_parent, wxID_ANY, _(name) + ":"); + set_font_and_background_style(m_move_Label, wxGetApp().normal_font()); + + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->SetMinSize(wxSize(-1, height)); + sizer->Add(*label, 0, wxALIGN_CENTER_VERTICAL); + + if (resive_sizer) + resive_sizer->Add(sizer); + else + m_labels_grid_sizer->Add(sizer); + }; + + // Add labels + add_label(&m_move_Label, L("Position")); + add_label(&m_rotate_Label, L("Rotation")); + + // additional sizer for lock and labels "Scale" & "Size" + sizer = new wxBoxSizer(wxHORIZONTAL); + + m_lock_bnt = new LockButton(parent, wxID_ANY); + m_lock_bnt->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + event.Skip(); + wxTheApp->CallAfter([this]() { set_uniform_scaling(m_lock_bnt->IsLocked()); }); + }); + sizer->Add(m_lock_bnt, 0, wxALIGN_CENTER_VERTICAL); + + auto v_sizer = new wxGridSizer(1, 3, 3); + + add_label(&m_scale_Label, L("Scale"), v_sizer); + wxStaticText* size_Label {nullptr}; + add_label(&size_Label, L("Size"), v_sizer); + + sizer->Add(v_sizer, 0, wxLEFT, border); + m_labels_grid_sizer->Add(sizer); + m_main_grid_sizer->Add(m_labels_grid_sizer, 0, wxEXPAND); + + + // Add editors grid sizer + m_editors_grid_sizer = new wxFlexGridSizer(5, 3, 3); // "Name/label", "String name / Editors" + m_editors_grid_sizer->SetFlexibleDirection(wxBOTH); + + // Add Axes labels with icons + static const char axes[] = { 'X', 'Y', 'Z' }; + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) { + const char label = axes[axis_idx]; + + wxStaticText* axis_name = new wxStaticText(m_parent, wxID_ANY, wxString(label)); + set_font_and_background_style(axis_name, wxGetApp().bold_font()); + + sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(axis_name, 0, wxALIGN_CENTER_VERTICAL/* | wxLEFT*/ | wxRIGHT, border); + + // We will add a button to toggle mirroring to each axis: + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); + btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label)); + btn->SetBitmapDisabled_(m_mirror_bitmap_hidden); + + m_mirror_buttons[axis_idx].first = btn; + m_mirror_buttons[axis_idx].second = mbShown; + + sizer->AddStretchSpacer(2); + sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL); + + btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent&) { + Axis axis = (Axis)(axis_idx + X); + if (m_mirror_buttons[axis_idx].second == mbHidden) + return; + + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis)); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()) { + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis)); + } + } + else + return; + + // Update mirroring at the GLVolumes. + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); + // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. + canvas->do_mirror(L("Set Mirror")); + UpdateAndShow(true); + }); + + m_editors_grid_sizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL); + } + + m_editors_grid_sizer->AddStretchSpacer(1); + m_editors_grid_sizer->AddStretchSpacer(1); + + // add EditBoxes + auto add_edit_boxes = [this, em, parent](std::string opt_key, int axis) + { + wxTextCtrl* editor = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, + wxDefaultPosition, wxSize(5 * em, wxDefaultCoord), + wxTE_PROCESS_ENTER); + + set_font_and_background_style(editor, wxGetApp().normal_font()); +#ifdef __WXOSX__ + editor->OSXDisableAllSmartSubstitutions(); +#endif // __WXOSX__ + m_editors_grid_sizer->Add(editor, 1, wxEXPAND); + }; + + // add Units + auto add_unit_text = [this, parent](std::string unit) + { + wxStaticText* unit_text = new wxStaticText(parent, wxID_ANY, _(unit)); + + set_font_and_background_style(unit_text, wxGetApp().normal_font()); + m_editors_grid_sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); + }; + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("position", axis_idx); + add_unit_text(L("mm")); + + // Add drop to bed button + m_drop_to_bed_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed")); + m_drop_to_bed_button->SetToolTip(_(L("Drop to bed"))); + m_drop_to_bed_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent& e) { + // ??? + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + + const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); + Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed"))); + change_position_value(0, diff.x()); + change_position_value(1, diff.y()); + change_position_value(2, diff.z()); + } + }); + m_editors_grid_sizer->Add(m_drop_to_bed_button); + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("rotation", axis_idx); + add_unit_text("°"); + + // Add reset rotation button + m_reset_rotation_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); + m_reset_rotation_button->SetToolTip(_(L("Reset rotation"))); + m_reset_rotation_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + Selection& selection = canvas->get_selection(); + + if (selection.is_single_volume() || selection.is_single_modifier()) { + GLVolume* volume = const_cast(selection.get_volume(*selection.get_volume_idxs().begin())); + volume->set_volume_rotation(Vec3d::Zero()); + } + else if (selection.is_single_full_instance()) { + for (unsigned int idx : selection.get_volume_idxs()) { + GLVolume* volume = const_cast(selection.get_volume(idx)); + volume->set_instance_rotation(Vec3d::Zero()); + } + } + else + return; + + // Update rotation at the GLVolumes. + selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); + selection.synchronize_unselected_volumes(); + // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. + canvas->do_rotate(L("Reset Rotation")); + + UpdateAndShow(true); + }); + m_editors_grid_sizer->Add(m_reset_rotation_button); + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("scale", axis_idx); + add_unit_text("%"); + + // Add reset scale button + m_reset_scale_button = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); + m_reset_scale_button->SetToolTip(_(L("Reset scale"))); + m_reset_scale_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale"))); + change_scale_value(0, 100.); + change_scale_value(1, 100.); + change_scale_value(2, 100.); + }); + m_editors_grid_sizer->Add(m_reset_scale_button); + + for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) + add_edit_boxes("size", axis_idx); + add_unit_text("mm"); + m_editors_grid_sizer->AddStretchSpacer(1); + + m_main_grid_sizer->Add(m_editors_grid_sizer, 1, wxEXPAND); + + m_og->sizer->Clear(true); + m_og->sizer->Add(m_main_grid_sizer, 1, wxEXPAND | wxALL, border); + + m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation"); + + // Load bitmaps to be used for the mirroring buttons: + m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); + m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); + m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); +} + +/* ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) #ifndef __APPLE__ @@ -180,7 +468,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : line = Line{ "", "" }; def.label = ""; def.type = coString; - def.width = field_width - mirror_btn_width;//field_width/*50*/; + def.width = field_width - mirror_btn_width; // Load bitmaps to be used for the mirroring buttons: m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); @@ -254,7 +542,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : ConfigOptionDef def; def.type = coFloat; def.set_default_value(new ConfigOptionFloat(0.0)); - def.width = field_width/*50*/; + def.width = field_width; if (option_name == "Scale") { // Add "uniform scaling" button in front of "Scale" option @@ -395,7 +683,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize()); }; } - +*/ void ObjectManipulation::Show(const bool show) @@ -407,9 +695,9 @@ void ObjectManipulation::Show(const bool show) if (show && wxGetApp().get_mode() != comSimple) { // Show the label and the name of the STL in simple mode only. // Label "Name: " - m_og->get_grid_sizer()->Show(size_t(0), false); + /*m_og->get_grid_sizer()*/m_main_grid_sizer->Show(size_t(0), false); // The actual name of the STL. - m_og->get_grid_sizer()->Show(size_t(1), false); + /*m_og->get_grid_sizer()*/m_main_grid_sizer->Show(size_t(1), false); } } @@ -417,6 +705,7 @@ void ObjectManipulation::Show(const bool show) // Show the "World Coordinates" / "Local Coordintes" Combo in Advanced / Expert mode only. bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple; m_word_local_combo->Show(show_world_local_combo); + m_empty_str->Show(!show_world_local_combo); } } @@ -687,6 +976,11 @@ void ObjectManipulation::emulate_kill_focus() } #endif // __APPLE__ +void ObjectManipulation::update_item_name(const wxString& item_name) +{ + m_item_name->SetLabel(item_name); +} + void ObjectManipulation::update_warning_icon_state(const wxString& tooltip) { m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); @@ -923,6 +1217,8 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value) void ObjectManipulation::msw_rescale() { + const int em = wxGetApp().em_unit(); + m_item_name->SetMinSize(wxSize(20*em, wxDefaultCoord)); msw_rescale_word_local_combo(m_word_local_combo); m_manifold_warning_bmp.msw_rescale(); @@ -936,10 +1232,50 @@ void ObjectManipulation::msw_rescale() m_reset_scale_button->msw_rescale(); m_reset_rotation_button->msw_rescale(); m_drop_to_bed_button->msw_rescale(); + m_lock_bnt->msw_rescale(); for (int id = 0; id < 3; ++id) m_mirror_buttons[id].first->msw_rescale(); + // rescale label-heights + // Text trick to grid sizer layout: + // Height of labels should be equivalent to the edit boxes + const int height = wxTextCtrl(parent(), wxID_ANY, "Br").GetBestHeight(-1); + int cells_cnt = m_labels_grid_sizer->GetEffectiveRowsCount(); + for (int i = 0; i < cells_cnt; i++) + { + const wxSizerItem* item = m_labels_grid_sizer->GetItem(i); + if (item->IsSizer()) + { + const wxSizerItem* label_item = item->GetSizer()->GetItem(size_t(0)); + if (label_item->IsWindow()) + { + if (dynamic_cast(label_item->GetWindow())) + item->GetSizer()->SetMinSize(wxSize(-1, height)); + if (dynamic_cast(label_item->GetWindow())) + item->GetSizer()->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); + else if (dynamic_cast(label_item->GetWindow())) // case when we have lock_btn and labels "Scale" and "Size" + { + const wxSizerItem* l_item = item->GetSizer()->GetItem(1); // sizer with labels "Scale" and "Size" + if (l_item->IsSizer()) { + for (int id : {0,1}) { + l_item->GetSizer()->GetItem(id)->GetSizer()->SetMinSize(wxSize(-1, height)); + } + } + } + } + } + } + + // rescale edit-boxes + cells_cnt = m_editors_grid_sizer->GetCols() * m_editors_grid_sizer->GetEffectiveRowsCount(); + for (int i = 0; i < cells_cnt; i++) + { + const wxSizerItem* item = m_editors_grid_sizer->GetItem(i); + if (item->IsWindow() && dynamic_cast(item->GetWindow())) + item->GetWindow()->SetMinSize(wxSize(5 * em, -1)); + } + get_og()->msw_rescale(); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index e4e190b5b..8ef6ca263 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -53,6 +53,9 @@ class ObjectManipulation : public OG_Settings wxStaticText* m_scale_Label = nullptr; wxStaticText* m_rotate_Label = nullptr; + wxStaticText* m_item_name = nullptr; + wxStaticText* m_empty_str = nullptr; + // Non-owning pointers to the reset buttons, so we can hide and show them. ScalableButton* m_reset_scale_button = nullptr; ScalableButton* m_reset_rotation_button = nullptr; @@ -81,7 +84,7 @@ class ObjectManipulation : public OG_Settings Vec3d m_new_rotation; Vec3d m_new_scale; Vec3d m_new_size; - bool m_new_enabled; + bool m_new_enabled {true}; bool m_uniform_scale {true}; // Does the object manipulation panel work in World or Local coordinates? bool m_world_coordinates = true; @@ -96,6 +99,10 @@ class ObjectManipulation : public OG_Settings std::string m_focused_option; #endif // __APPLE__ + wxFlexGridSizer* m_main_grid_sizer; + wxFlexGridSizer* m_labels_grid_sizer; + wxFlexGridSizer* m_editors_grid_sizer; + public: ObjectManipulation(wxWindow* parent); ~ObjectManipulation() {} @@ -122,6 +129,7 @@ public: void emulate_kill_focus(); #endif // __APPLE__ + void update_item_name(const wxString &item_name); void update_warning_icon_state(const wxString& tooltip); void msw_rescale(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index bf5500cf4..79b398f2e 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -2987,6 +2987,8 @@ void LockButton::msw_rescale() m_bmp_lock_closed_f.msw_rescale(); m_bmp_lock_open.msw_rescale(); m_bmp_lock_open_f.msw_rescale(); + + update_button_bitmaps(); } void LockButton::update_button_bitmaps() From 67d7809418a5ae6bdf339077a79847045d3a2c0e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 19 Sep 2019 08:44:19 +0200 Subject: [PATCH 02/23] Fixed font size and layouts for ObjectManipulation panel under OSX --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 963ae53ab..f75d32359 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -183,7 +183,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Height of labels should be equivalent to the edit boxes int height = wxTextCtrl(parent, wxID_ANY, "Br").GetBestHeight(-1); - auto add_label = [this, height](wxStaticText** label, std::string name, wxSizer* resive_sizer = nullptr) + auto add_label = [this, height](wxStaticText** label, const std::string& name, wxSizer* resive_sizer = nullptr) { *label = new wxStaticText(m_parent, wxID_ANY, _(name) + ":"); set_font_and_background_style(m_move_Label, wxGetApp().normal_font()); @@ -217,6 +217,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : add_label(&m_scale_Label, L("Scale"), v_sizer); wxStaticText* size_Label {nullptr}; add_label(&size_Label, L("Size"), v_sizer); + if (wxOSX) set_font_and_background_style(size_Label, wxGetApp().normal_font()); sizer->Add(v_sizer, 0, wxLEFT, border); m_labels_grid_sizer->Add(sizer); @@ -236,7 +237,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : set_font_and_background_style(axis_name, wxGetApp().bold_font()); sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(axis_name, 0, wxALIGN_CENTER_VERTICAL/* | wxLEFT*/ | wxRIGHT, border); + // Under OSX we use font, smaller than default font, so + // there is a next trick for an equivalent layout of coordinates combobox and axes labels in they own sizers + if (wxOSX) + sizer->SetMinSize(-1, m_word_local_combo->GetBestHeight(-1)); + sizer->Add(axis_name, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border); // We will add a button to toggle mirroring to each axis: auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); @@ -811,6 +816,7 @@ void ObjectManipulation::update_if_dirty() if (label_cache != new_label_localized) { label_cache = new_label_localized; widget->SetLabel(new_label_localized); + if (wxOSX) set_font_and_background_style(widget, wxGetApp().normal_font()); } }; update_label(m_cache.move_label_string, m_new_move_label_string, m_move_Label); From 6faf67d90cdb31f05758cf12f68a97ad1b01c607 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 25 Sep 2019 11:25:05 +0200 Subject: [PATCH 03/23] Add binding for ManipulationEditors --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 209 ++++++++++++++++++---- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 26 ++- 2 files changed, 199 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index f75d32359..9b4795a3e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -124,6 +124,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : , m_focused_option("") #endif // __APPLE__ { + m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation"); + + // Load bitmaps to be used for the mirroring buttons: + m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); + m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); + m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); + const int border = wxOSX ? 0 : 4; const int em = wxGetApp().em_unit(); m_main_grid_sizer = new wxFlexGridSizer(2, 3, 3); // "Name/label", "String name / Editors" @@ -225,8 +232,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Add editors grid sizer - m_editors_grid_sizer = new wxFlexGridSizer(5, 3, 3); // "Name/label", "String name / Editors" - m_editors_grid_sizer->SetFlexibleDirection(wxBOTH); + wxFlexGridSizer* editors_grid_sizer = new wxFlexGridSizer(5, 3, 3); // "Name/label", "String name / Editors" + editors_grid_sizer->SetFlexibleDirection(wxBOTH); // Add Axes labels with icons static const char axes[] = { 'X', 'Y', 'Z' }; @@ -283,33 +290,28 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : UpdateAndShow(true); }); - m_editors_grid_sizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL); + editors_grid_sizer->Add(sizer, 0, wxALIGN_CENTER_HORIZONTAL); } - m_editors_grid_sizer->AddStretchSpacer(1); - m_editors_grid_sizer->AddStretchSpacer(1); + editors_grid_sizer->AddStretchSpacer(1); + editors_grid_sizer->AddStretchSpacer(1); // add EditBoxes - auto add_edit_boxes = [this, em, parent](std::string opt_key, int axis) + auto add_edit_boxes = [this, editors_grid_sizer](const std::string& opt_key, int axis) { - wxTextCtrl* editor = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, - wxDefaultPosition, wxSize(5 * em, wxDefaultCoord), - wxTE_PROCESS_ENTER); + ManipulationEditor* editor = new ManipulationEditor(this, opt_key, axis); + m_editors.push_back(editor); - set_font_and_background_style(editor, wxGetApp().normal_font()); -#ifdef __WXOSX__ - editor->OSXDisableAllSmartSubstitutions(); -#endif // __WXOSX__ - m_editors_grid_sizer->Add(editor, 1, wxEXPAND); + editors_grid_sizer->Add(editor, 1, wxEXPAND); }; // add Units - auto add_unit_text = [this, parent](std::string unit) + auto add_unit_text = [this, parent, editors_grid_sizer](std::string unit) { wxStaticText* unit_text = new wxStaticText(parent, wxID_ANY, _(unit)); set_font_and_background_style(unit_text, wxGetApp().normal_font()); - m_editors_grid_sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); + editors_grid_sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); }; for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) @@ -336,7 +338,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_position_value(2, diff.z()); } }); - m_editors_grid_sizer->Add(m_drop_to_bed_button); + editors_grid_sizer->Add(m_drop_to_bed_button); for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) add_edit_boxes("rotation", axis_idx); @@ -370,7 +372,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : UpdateAndShow(true); }); - m_editors_grid_sizer->Add(m_reset_rotation_button); + editors_grid_sizer->Add(m_reset_rotation_button); for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) add_edit_boxes("scale", axis_idx); @@ -385,24 +387,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_scale_value(1, 100.); change_scale_value(2, 100.); }); - m_editors_grid_sizer->Add(m_reset_scale_button); + editors_grid_sizer->Add(m_reset_scale_button); for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) add_edit_boxes("size", axis_idx); add_unit_text("mm"); - m_editors_grid_sizer->AddStretchSpacer(1); + editors_grid_sizer->AddStretchSpacer(1); - m_main_grid_sizer->Add(m_editors_grid_sizer, 1, wxEXPAND); + m_main_grid_sizer->Add(editors_grid_sizer, 1, wxEXPAND); m_og->sizer->Clear(true); m_og->sizer->Add(m_main_grid_sizer, 1, wxEXPAND | wxALL, border); - - m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation"); - - // Load bitmaps to be used for the mirroring buttons: - m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); - m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); - m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); } /* @@ -823,6 +818,7 @@ void ObjectManipulation::update_if_dirty() update_label(m_cache.rotate_label_string, m_new_rotate_label_string, m_rotate_Label); update_label(m_cache.scale_label_string, m_new_scale_label_string, m_scale_Label); + /* char axis[2] = "x"; for (int i = 0; i < 3; ++ i, ++ axis[0]) { auto update = [this, i, &axis](Vec3d &cached, Vec3d &cached_rounded, const char *key, const Vec3d &new_value) { @@ -840,6 +836,34 @@ void ObjectManipulation::update_if_dirty() update(m_cache.size, m_cache.size_rounded, "size_", m_new_size); update(m_cache.rotation, m_cache.rotation_rounded, "rotation_", m_new_rotation); } + */ + + enum ManipulationEditorKey + { + mePosition = 0, + meRotation, + meScale, + meSize + }; + + for (int i = 0; i < 3; ++ i) { + auto update = [this, i](Vec3d &cached, Vec3d &cached_rounded, ManipulationEditorKey key_id, const Vec3d &new_value) { + wxString new_text = double_to_string(new_value(i), 2); + double new_rounded; + new_text.ToDouble(&new_rounded); + if (std::abs(cached_rounded(i) - new_rounded) > EPSILON) { + cached_rounded(i) = new_rounded; + const int id = key_id*3+i; + if (id >= 0) m_editors[id]->set_value(new_text); + } + cached(i) = new_value(i); + }; + update(m_cache.position, m_cache.position_rounded, mePosition, m_new_position); + update(m_cache.scale, m_cache.scale_rounded, meScale, m_new_scale); + update(m_cache.size, m_cache.size_rounded, meSize, m_new_size); + update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); + } + if (selection.requires_uniform_scale()) { m_lock_bnt->SetLock(true); @@ -1151,6 +1175,21 @@ void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any change_size_value(axis, new_value); } +void ObjectManipulation::on_change(const std::string& opt_key, int axis, double new_value) +{ + if (!m_cache.is_valid()) + return; + + if (opt_key == "position") + change_position_value(axis, new_value); + else if (opt_key == "rotation") + change_rotation_value(axis, new_value); + else if (opt_key == "scale") + change_scale_value(axis, new_value); + else if (opt_key == "size") + change_size_value(axis, new_value); +} + void ObjectManipulation::on_fill_empty_value(const std::string& opt_key) { // needed to hide the visual hints in 3D scene @@ -1274,16 +1313,116 @@ void ObjectManipulation::msw_rescale() } // rescale edit-boxes - cells_cnt = m_editors_grid_sizer->GetCols() * m_editors_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { - const wxSizerItem* item = m_editors_grid_sizer->GetItem(i); - if (item->IsWindow() && dynamic_cast(item->GetWindow())) - item->GetWindow()->SetMinSize(wxSize(5 * em, -1)); - } + for (ManipulationEditor* editor : m_editors) + editor->msw_rescale(); get_og()->msw_rescale(); } +static const char axes[] = { 'x', 'y', 'z' }; +ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, + const std::string& opt_key, + int axis) : + wxTextCtrl(parent->parent(), wxID_ANY, wxEmptyString, wxDefaultPosition, + wxSize(5*int(wxGetApp().em_unit()), wxDefaultCoord), wxTE_PROCESS_ENTER), + m_opt_key(opt_key), + m_axis(axis) +{ + set_font_and_background_style(this, wxGetApp().normal_font()); +#ifdef __WXOSX__ + this->OSXDisableAllSmartSubstitutions(); +#endif // __WXOSX__ + + // A name used to call handle_sidebar_focus_event() + m_full_opt_name = m_opt_key+"_"+axes[axis]; + + // Reset m_enter_pressed flag to _false_, when value is editing + this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId()); + + this->Bind(wxEVT_TEXT_ENTER, [this, parent](wxEvent&) + { + m_enter_pressed = true; + parent->on_change(m_opt_key, m_axis, get_value()); + }, this->GetId()); + + this->Bind(wxEVT_KILL_FOCUS, [this, parent/*, edit_fn*/](wxFocusEvent& e) + { + if (!m_enter_pressed) { + parent->on_change(m_opt_key, m_axis, get_value()); + + // if the change does not come from the user pressing the ENTER key + // we need to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, false); +// #ifndef __WXGTK__ +// /* Update data for next editor selection. +// * But under GTK it looks like there is no information about selected control at e.GetWindow(), +// * so we'll take it from wxEVT_LEFT_DOWN event +// * */ +// LayerRangeEditor* new_editor = dynamic_cast(e.GetWindow()); +// if (new_editor) +// new_editor->set_focus_data(); +// #endif // not __WXGTK__ + } + + e.Skip(); + }, this->GetId()); + + this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& e) + { + // needed to show the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(m_full_opt_name, true); + e.Skip(); + }, this->GetId()); + +// #ifdef __WXGTK__ // Workaround! To take information about selectable range +// this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e) +// { +// set_focus_data(); +// e.Skip(); +// }, this->GetId()); +// #endif //__WXGTK__ + + this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event) + { + // select all text using Ctrl+A + if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) + this->SetSelection(-1, -1); //select all + event.Skip(); + })); +} + +void ManipulationEditor::msw_rescale() +{ + const int em = wxGetApp().em_unit(); + SetMinSize(wxSize(5 * em, wxDefaultCoord)); +} + +double ManipulationEditor::get_value() +{ + wxString str = GetValue(); + + double value; + // Replace the first occurence of comma in decimal number. + str.Replace(",", ".", false); + if (str == ".") + value = 0.0; + + if ((str.IsEmpty() || !str.ToCDouble(&value)) && !m_valid_value.IsEmpty()) { + str = m_valid_value; + SetValue(str); + str.ToCDouble(&value); + } + + return value; +} + +void ManipulationEditor::set_value(const wxString& new_value) +{ + if (new_value.IsEmpty()) + return; + m_valid_value = new_value; + SetValue(m_valid_value); +} + } //namespace GUI } //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 8ef6ca263..d0fe33dab 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -16,6 +16,28 @@ namespace GUI { class Selection; +class ObjectManipulation; +class ManipulationEditor : public wxTextCtrl +{ + std::string m_opt_key; + int m_axis; + bool m_enter_pressed { false }; + wxString m_valid_value {wxEmptyString}; + + std::string m_full_opt_name; + +public: + ManipulationEditor(ObjectManipulation* parent, const std::string& opt_key, int axis); + ~ManipulationEditor() {} + + void msw_rescale(); + void set_value(const wxString& new_value); + +private: + double get_value(); +}; + + class ObjectManipulation : public OG_Settings { struct Cache @@ -101,7 +123,8 @@ class ObjectManipulation : public OG_Settings wxFlexGridSizer* m_main_grid_sizer; wxFlexGridSizer* m_labels_grid_sizer; - wxFlexGridSizer* m_editors_grid_sizer; + + std::vector m_editors; public: ObjectManipulation(wxWindow* parent); @@ -132,6 +155,7 @@ public: void update_item_name(const wxString &item_name); void update_warning_icon_state(const wxString& tooltip); void msw_rescale(); + void on_change(const std::string& opt_key, int axis, double new_value); private: void reset_settings_value(); From f6996b7dda5aeb8671165e57a4ad8de63e7628f4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 25 Sep 2019 12:45:39 +0200 Subject: [PATCH 04/23] Try to fix Linux build --- src/admesh/stl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 43999d365..a6989ca6e 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -90,7 +90,7 @@ struct stl_neighbors { struct stl_stats { stl_stats() { memset(&header, 0, 81); } - char header[81] = ""; + char header[81];// = ""; stl_type type = (stl_type)0; uint32_t number_of_facets = 0; stl_vertex max = stl_vertex::Zero(); From 1a03b664b97fe129230cec75ebcc7c0e0c03d8df Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 25 Sep 2019 15:04:39 +0200 Subject: [PATCH 05/23] Some changes for alignments in manipulation panel --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 67 ++++++++++------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 6 +- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 9b4795a3e..747cec0f9 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -179,18 +179,25 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Small trick to correct layouting in different view_mode : // Show empty string of a same height as a m_word_local_combo, when m_word_local_combo is hidden - sizer = new wxBoxSizer(wxHORIZONTAL); + m_word_local_combo_sizer = new wxBoxSizer(wxHORIZONTAL); m_empty_str = new wxStaticText(parent, wxID_ANY, ""); - sizer->Add(m_word_local_combo); - sizer->Add(m_empty_str); - sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); - m_labels_grid_sizer->Add(sizer); + m_word_local_combo_sizer->Add(m_word_local_combo); + m_word_local_combo_sizer->Add(m_empty_str); + m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); + m_labels_grid_sizer->Add(m_word_local_combo_sizer); // Text trick to grid sizer layout: // Height of labels should be equivalent to the edit boxes int height = wxTextCtrl(parent, wxID_ANY, "Br").GetBestHeight(-1); +#ifdef __WXGTK__ + // On Linux button with bitmap has bigger height then regular button or regular TextCtrl + // It can cause a wrong alignment on show/hide of a reset buttons + const int bmp_btn_height = ScalableButton(parent, wxID_ANY, "undo") .GetBestHeight(-1); + if (bmp_btn_height > height) + height = bmp_btn_height; +#endif //__WXGTK__ - auto add_label = [this, height](wxStaticText** label, const std::string& name, wxSizer* resive_sizer = nullptr) + auto add_label = [this, height](wxStaticText** label, const std::string& name, wxSizer* reciver = nullptr) { *label = new wxStaticText(m_parent, wxID_ANY, _(name) + ":"); set_font_and_background_style(m_move_Label, wxGetApp().normal_font()); @@ -199,10 +206,12 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : sizer->SetMinSize(wxSize(-1, height)); sizer->Add(*label, 0, wxALIGN_CENTER_VERTICAL); - if (resive_sizer) - resive_sizer->Add(sizer); + if (reciver) + reciver->Add(sizer); else m_labels_grid_sizer->Add(sizer); + + m_rescalable_sizers.push_back(sizer); }; // Add labels @@ -302,16 +311,22 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : ManipulationEditor* editor = new ManipulationEditor(this, opt_key, axis); m_editors.push_back(editor); - editors_grid_sizer->Add(editor, 1, wxEXPAND); + editors_grid_sizer->Add(editor, 0, wxALIGN_CENTER_VERTICAL); }; // add Units - auto add_unit_text = [this, parent, editors_grid_sizer](std::string unit) + auto add_unit_text = [this, parent, editors_grid_sizer, height](std::string unit) { wxStaticText* unit_text = new wxStaticText(parent, wxID_ANY, _(unit)); + set_font_and_background_style(unit_text, wxGetApp().normal_font()); - set_font_and_background_style(unit_text, wxGetApp().normal_font()); - editors_grid_sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); + // Unit text should be the same height as labels + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->SetMinSize(wxSize(-1, height)); + sizer->Add(unit_text, 0, wxALIGN_CENTER_VERTICAL); + + editors_grid_sizer->Add(sizer); + m_rescalable_sizers.push_back(sizer); }; for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) @@ -1265,6 +1280,7 @@ void ObjectManipulation::msw_rescale() const int em = wxGetApp().em_unit(); m_item_name->SetMinSize(wxSize(20*em, wxDefaultCoord)); msw_rescale_word_local_combo(m_word_local_combo); + m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); m_manifold_warning_bmp.msw_rescale(); const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText(); @@ -1286,31 +1302,8 @@ void ObjectManipulation::msw_rescale() // Text trick to grid sizer layout: // Height of labels should be equivalent to the edit boxes const int height = wxTextCtrl(parent(), wxID_ANY, "Br").GetBestHeight(-1); - int cells_cnt = m_labels_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; i++) - { - const wxSizerItem* item = m_labels_grid_sizer->GetItem(i); - if (item->IsSizer()) - { - const wxSizerItem* label_item = item->GetSizer()->GetItem(size_t(0)); - if (label_item->IsWindow()) - { - if (dynamic_cast(label_item->GetWindow())) - item->GetSizer()->SetMinSize(wxSize(-1, height)); - if (dynamic_cast(label_item->GetWindow())) - item->GetSizer()->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); - else if (dynamic_cast(label_item->GetWindow())) // case when we have lock_btn and labels "Scale" and "Size" - { - const wxSizerItem* l_item = item->GetSizer()->GetItem(1); // sizer with labels "Scale" and "Size" - if (l_item->IsSizer()) { - for (int id : {0,1}) { - l_item->GetSizer()->GetItem(id)->GetSizer()->SetMinSize(wxSize(-1, height)); - } - } - } - } - } - } + for (wxBoxSizer* sizer : m_rescalable_sizers) + sizer->SetMinSize(wxSize(-1, height)); // rescale edit-boxes for (ManipulationEditor* editor : m_editors) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index d0fe33dab..e25aab678 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -124,7 +124,11 @@ class ObjectManipulation : public OG_Settings wxFlexGridSizer* m_main_grid_sizer; wxFlexGridSizer* m_labels_grid_sizer; - std::vector m_editors; + // sizers, used for msw_rescale + wxBoxSizer* m_word_local_combo_sizer; + std::vector m_rescalable_sizers; + + std::vector m_editors; public: ObjectManipulation(wxWindow* parent); From c07a193b4ef28b08f939feba4164966f2defc43d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 30 Sep 2019 14:03:50 +0200 Subject: [PATCH 06/23] Implemented BitmapChoiseRenderer --- src/slic3r/GUI/Plater.cpp | 19 +++-- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/wxExtensions.cpp | 136 +++++++++++++++++++++++++++++++- src/slic3r/GUI/wxExtensions.hpp | 34 ++++++++ 4 files changed, 183 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b172ad489..53287eedf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -521,12 +521,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : const std::vector &init_matrix = (project_config.option("wiping_volumes_matrix"))->values; const std::vector &init_extruders = (project_config.option("wiping_volumes_extruders"))->values; - const DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; - std::vector extruder_colours = (config->option("extruder_colour"))->values; - const std::vector& filament_colours = (wxGetApp().plater()->get_plater_config()->option("filament_colour"))->values; - for (size_t i=0; i extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); WipingDialog dlg(parent, cast(init_matrix), cast(init_extruders), extruder_colours); @@ -4891,6 +4886,18 @@ const DynamicPrintConfig* Plater::get_plater_config() const return p->config; } +std::vector Plater::get_extruder_colors_from_plater_config() const +{ + const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; + std::vector extruder_colors = (config->option("extruder_colour"))->values; + const std::vector& filament_colours = (p->config->option("filament_colour"))->values; + for (size_t i = 0; i < extruder_colors.size(); ++i) + if (extruder_colors[i] == "" && i < filament_colours.size()) + extruder_colors[i] = filament_colours[i]; + + return extruder_colors; +} + wxString Plater::get_project_filename(const wxString& extension) const { return p->get_project_filename(extension); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 93df68738..f19ac9e77 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -215,6 +215,7 @@ public: // On activating the parent window. void on_activate(); const DynamicPrintConfig* get_plater_config() const; + std::vector get_extruder_colors_from_plater_config() const; void update_object_menu(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 59406b6e9..8e4e3520a 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -7,6 +7,7 @@ #include "libslic3r/Model.hpp" #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "libslic3r/GCode/PreviewData.hpp" #include "I18N.hpp" #include "GUI_Utils.hpp" +#include "PresetBundle.hpp" #include "../Utils/MacDarkMode.hpp" using Slic3r::GUI::from_u8; @@ -1840,7 +1842,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo } //----------------------------------------------------------------------------- -// PrusaDataViewBitmapText +// DataViewBitmapText //----------------------------------------------------------------------------- wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) @@ -1966,6 +1968,138 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value return true; } +// ---------------------------------------------------------------------------- +// BitmapChoiseRenderer +// ---------------------------------------------------------------------------- + +bool BitmapChoiseRenderer::SetValue(const wxVariant& value) +{ + m_value << value; + return true; +} +bool BitmapChoiseRenderer::GetValue(wxVariant& value) const +{ + value << m_value; + return true; +} +bool BitmapChoiseRenderer::Render(wxRect rect, wxDC* dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapChoiseRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); + + /* from wxDataViewChoiceRenderer + wxSize sz; + + for ( wxArrayString::const_iterator i = m_choices.begin(); i != m_choices.end(); ++i ) + sz.IncTo(GetTextExtent(*i)); + + // Allow some space for the right-side button, which is approximately the + // size of a scrollbar (and getting pixel-exact value would be complicated). + // Also add some whitespace between the text and the button: + sz.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + sz.x += GetTextExtent("M").x; + + return sz; + */ +} + +static void update_extruder_color_icons_in_cache() +{ + // Create the bitmap with color bars. + std::vector bmps; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + unsigned char rgb[3]; + + /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const double em = Slic3r::GUI::wxGetApp().em_unit(); + const int icon_width = lround(3.6 * em); + const int icon_height = lround(1.6 * em); + + for (const std::string& color : colors) + { + wxBitmap* bitmap = m_bitmap_cache->find(color); + if (bitmap == nullptr) { + // Paint the color icon. + Slic3r::PresetBundle::parse_color(color, rgb); + bitmap = m_bitmap_cache->insert(color, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); + } + bmps.emplace_back(bitmap); + } +} + + +wxWindow* BitmapChoiseRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); + ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); + + if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject))) + return nullptr; + + DataViewBitmapText data; + data << value; + +// m_was_unusable_symbol = false; + + wxPoint position = labelRect.GetPosition(); + if (data.GetBitmap().IsOk()) { + const int bmp_width = data.GetBitmap().GetWidth(); + position.x += bmp_width; + labelRect.SetWidth(labelRect.GetWidth() - bmp_width); + } + + auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, //data.GetText(), + labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), + 0, nullptr , wxCB_READONLY); + + c_editor->Move(labelRect.GetRight() - c_editor->GetRect().width, wxDefaultCoord); + c_editor->SetStringSelection(data.GetText()); + return c_editor; +} + +bool BitmapChoiseRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; + int selection = c->GetSelection(); + if (selection < 0) + return false; + + DataViewBitmapText bmpText; + + bmpText.SetText(c->GetString(selection)); + bmpText.SetBitmap(c->GetItemBitmap(selection)); + + value << bmpText; + return true; +} + // ---------------------------------------------------------------------------- // DoubleSlider // ---------------------------------------------------------------------------- diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 54d1bf7cb..3d90e966c 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -563,6 +563,40 @@ private: }; +// ---------------------------------------------------------------------------- +// BitmapChoiseRenderer +// ---------------------------------------------------------------------------- + +class BitmapChoiseRenderer : public wxDataViewCustomRenderer +{ +public: + BitmapChoiseRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT + + , int align = wxDVR_DEFAULT_ALIGNMENT + ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const override; +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + virtual bool Render(wxRect cell, wxDC* dc, int state); + virtual wxSize GetSize() const; + + bool HasEditorCtrl() const override { return true; } + wxWindow* CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, + wxVariant& value) override; + +private: + DataViewBitmapText m_value; + wxArrayString m_choices; +}; + + // ---------------------------------------------------------------------------- // MyCustomRenderer // ---------------------------------------------------------------------------- From d7ebc4de5baca66df1820627222a804caa2568df Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 1 Oct 2019 18:19:28 +0200 Subject: [PATCH 07/23] Added color for extruder --- src/slic3r/GUI/GUI_ObjectList.cpp | 47 ++---- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 6 +- src/slic3r/GUI/wxExtensions.cpp | 242 +++++++++++++++++++++--------- src/slic3r/GUI/wxExtensions.hpp | 37 ++--- 5 files changed, 208 insertions(+), 126 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 19dedc7b0..7d650dcd1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -267,7 +267,8 @@ void ObjectList::create_objects_ctrl() wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // column Extruder of the view control: - AppendColumn(create_objects_list_extruder_column(4)); + AppendColumn(new wxDataViewColumn(_(L("Extruder")), new BitmapChoiceRenderer(), + colExtruder, 8*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE)); // column ItemEditing of the view control: AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em, @@ -434,19 +435,6 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons (*m_objects)[obj_idx]->config; } -wxDataViewColumn* ObjectList::create_objects_list_extruder_column(size_t extruders_count) -{ - wxArrayString choices; - choices.Add(_(L("default"))); - for (int i = 1; i <= extruders_count; ++i) - choices.Add(wxString::Format("%d", i)); - wxDataViewChoiceRenderer *c = - new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL); - wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, colExtruder, - 8*wxGetApp().em_unit()/*80*/, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - return column; -} - void ObjectList::update_extruder_values_for_items(const size_t max_extruder) { for (size_t i = 0; i < m_objects->size(); ++i) @@ -462,7 +450,7 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder) else extruder = wxString::Format("%d", object->config.option("extruder")->value); - m_objects_model->SetValue(extruder, item, colExtruder); + m_objects_model->SetExtruder(extruder, item); if (object->volumes.size() > 1) { for (size_t id = 0; id < object->volumes.size(); id++) { @@ -474,7 +462,7 @@ void ObjectList::update_extruder_values_for_items(const size_t max_extruder) else extruder = wxString::Format("%d", object->volumes[id]->config.option("extruder")->value); - m_objects_model->SetValue(extruder, item, colExtruder); + m_objects_model->SetExtruder(extruder, item); } } } @@ -486,19 +474,13 @@ void ObjectList::update_objects_list_extruder_column(size_t extruders_count) if (printer_technology() == ptSLA) extruders_count = 1; - wxDataViewChoiceRenderer* ch_render = dynamic_cast(GetColumn(colExtruder)->GetRenderer()); - if (ch_render->GetChoices().GetCount() - 1 == extruders_count) - return; - m_prevent_update_extruder_in_config = true; if (m_objects && extruders_count > 1) update_extruder_values_for_items(extruders_count); - // delete old extruder column - DeleteColumn(GetColumn(colExtruder)); - // insert new created extruder column - InsertColumn(colExtruder, create_objects_list_extruder_column(extruders_count)); + update_extruder_colors(); + // set show/hide for this column set_extruder_column_hidden(extruders_count <= 1); //a workaround for a wrong last column width updating under OSX @@ -507,6 +489,11 @@ void ObjectList::update_objects_list_extruder_column(size_t extruders_count) m_prevent_update_extruder_in_config = false; } +void ObjectList::update_extruder_colors() +{ + m_objects_model->UpdateColumValues(colExtruder); +} + void ObjectList::set_extruder_column_hidden(const bool hide) const { GetColumn(colExtruder)->SetHidden(hide); @@ -535,14 +522,10 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item) m_config = &get_item_config(item); } - wxVariant variant; - m_objects_model->GetValue(variant, item, colExtruder); - const wxString selection = variant.GetString(); - - if (!m_config || selection.empty()) + if (!m_config) return; - const int extruder = /*selection.size() > 1 ? 0 : */atoi(selection.c_str()); + const int extruder = m_objects_model->GetExtruderNumber(item); m_config->set_key_value("extruder", new ConfigOptionInt(extruder)); // update scene @@ -2547,7 +2530,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it (*m_objects)[item->obj_idx]->config.has("extruder")) { const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option("extruder")->value); - m_objects_model->SetValue(extruder, m_objects_model->GetItemById(item->obj_idx), colExtruder); + m_objects_model->SetExtruder(extruder, m_objects_model->GetItemById(item->obj_idx)); } wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx); } @@ -3822,7 +3805,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const /* We can change extruder for Object/Volume only. * So, if Instance is selected, get its Object item and change it */ - m_objects_model->SetValue(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item, colExtruder); + m_objects_model->SetExtruder(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item); const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 4dd618a90..b89d8943d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -183,8 +183,8 @@ public: void create_objects_ctrl(); void create_popup_menus(); - wxDataViewColumn* create_objects_list_extruder_column(size_t extruders_count); void update_objects_list_extruder_column(size_t extruders_count); + void update_extruder_colors(); // show/hide "Extruder" column for Objects List void set_extruder_column_hidden(const bool hide) const; // update extruder in current config diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 53287eedf..baf84befc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4785,6 +4785,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); p->config->option(opt_key)->values = filament_colors; + p->sidebar->obj_list()->update_extruder_colors(); continue; } } @@ -4810,6 +4811,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) else if(opt_key == "extruder_colour") { update_scheduled = true; p->preview->set_number_extruders(p->config->option(opt_key)->values.size()); + p->sidebar->obj_list()->update_extruder_colors(); } else if(opt_key == "max_print_height") { update_scheduled = true; } @@ -4858,8 +4860,10 @@ void Plater::force_filament_colors_update() } } - if (update_scheduled) + if (update_scheduled) { update(); + p->sidebar->obj_list()->update_extruder_colors(); + } if (p->main_frame->is_loaded()) this->p->schedule_background_process(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 8e4e3520a..b692d1cae 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -447,6 +447,47 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, return *bmp; } + +Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; +static std::vector get_extruder_color_icons() +{ + // Create the bitmap with color bars. + std::vector bmps; + std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + unsigned char rgb[3]; + + /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const double em = Slic3r::GUI::wxGetApp().em_unit(); + const int icon_width = lround(3.2 * em); + const int icon_height = lround(1.6 * em); + + for (const std::string& color : colors) + { + wxBitmap* bitmap = m_bitmap_cache->find(color); + if (bitmap == nullptr) { + // Paint the color icon. + Slic3r::PresetBundle::parse_color(color, rgb); + bitmap = m_bitmap_cache->insert(color, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); + } + bmps.emplace_back(bitmap); + } + + return bmps; +} + + +static wxBitmap get_extruder_color_icon(size_t extruder_idx) +{ + // Create the bitmap with color bars. + std::vector bmps = get_extruder_color_icons(); + + return *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx]; +} + // ***************************************************************************** // ---------------------------------------------------------------------------- // ObjectDataViewModelNode @@ -479,7 +520,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_idx = parent->GetChildCount(); m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); - set_action_icon(); + set_action_and_extruder_icons(); } else if (type == itLayerRoot) { @@ -514,7 +555,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; m_bmp = create_scaled_bitmap(nullptr, LAYER_ICON); // FIXME: pass window ptr - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -527,11 +568,16 @@ bool ObjectDataViewModelNode::valid() } #endif /* NDEBUG */ -void ObjectDataViewModelNode::set_action_icon() +void ObjectDataViewModelNode::set_action_and_extruder_icons() { m_action_icon_name = m_type & itObject ? "advanced_plus" : m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj"; m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr + + // set extruder bitmap + int extruder_idx = atoi(m_extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + m_extruder_bmp = get_extruder_color_icon(extruder_idx); } void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) @@ -541,7 +587,6 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); } -Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; void ObjectDataViewModelNode::update_settings_digest_bitmaps() { m_bmp = m_empty_bmp; @@ -607,8 +652,10 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) m_name = data.GetText(); return true; } case colExtruder: { - const wxString & val = variant.GetString(); - m_extruder = val == "0" ? _(L("default")) : val; + DataViewBitmapText data; + data << variant; + m_extruder_bmp = data.GetBitmap(); + m_extruder = data.GetText() == "0" ? _(L("default")) : data.GetText(); return true; } case colEditing: m_action_icon << variant; @@ -1381,6 +1428,51 @@ t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewIt return node->GetLayerRange(); } +bool ObjectDataViewModel::UpdateColumValues(unsigned col) +{ + switch (col) + { + case colPrint: + case colName: + case colEditing: + return true; + case colExtruder: + { + wxDataViewItemArray items; + GetAllChildren(wxDataViewItem(nullptr), items); + + if (items.IsEmpty()) return false; + + for (auto item : items) + UpdateExtruderBitmap(item); + + return true; + } + default: + printf("MyObjectTreeModel::SetValue: wrong column"); + } + return false; +} + + +void ObjectDataViewModel::UpdateExtruderBitmap(wxDataViewItem item) +{ + wxString extruder = GetExtruder(item); + if (extruder.IsEmpty()) + return; + + // set extruder bitmap + int extruder_idx = atoi(extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + + const DataViewBitmapText extruder_val(extruder, get_extruder_color_icon(extruder_idx)); + + wxVariant value; + value << extruder_val; + + SetValue(value, item, colExtruder); +} + void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) { wxASSERT(item.IsOk()); @@ -1477,6 +1569,24 @@ wxBitmap& ObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const return node->m_bmp; } +wxString ObjectDataViewModel::GetExtruder(const wxDataViewItem& item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return wxEmptyString; + + return node->m_extruder; +} + +int ObjectDataViewModel::GetExtruderNumber(const wxDataViewItem& item) const +{ + ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); + if (!node) // happens if item.IsOk()==false + return 0; + + return atoi(node->m_extruder.c_str()); +} + void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const { wxASSERT(item.IsOk()); @@ -1491,7 +1601,7 @@ void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &ite variant << DataViewBitmapText(node->m_name, node->m_bmp); break; case colExtruder: - variant = node->m_extruder; + variant << DataViewBitmapText(node->m_extruder, node->m_extruder_bmp); break; case colEditing: variant << node->m_action_icon; @@ -1517,6 +1627,22 @@ bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, return m_objects[item_idx]->SetValue(variant, col); } +void ObjectDataViewModel::SetExtruder(const wxString& extruder, wxDataViewItem item) +{ + DataViewBitmapText extruder_val; + extruder_val.SetText(extruder); + + // set extruder bitmap + int extruder_idx = atoi(extruder.c_str()); + if (extruder_idx > 0) --extruder_idx; + extruder_val.SetBitmap(get_extruder_color_icon(extruder_idx)); + + wxVariant value; + value << extruder_val; + + SetValue(value, item, colExtruder); +} + wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume_id, const int new_volume_id, const wxDataViewItem &parent) @@ -1969,20 +2095,22 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value } // ---------------------------------------------------------------------------- -// BitmapChoiseRenderer +// BitmapChoiceRenderer // ---------------------------------------------------------------------------- -bool BitmapChoiseRenderer::SetValue(const wxVariant& value) +bool BitmapChoiceRenderer::SetValue(const wxVariant& value) { m_value << value; return true; } -bool BitmapChoiseRenderer::GetValue(wxVariant& value) const + +bool BitmapChoiceRenderer::GetValue(wxVariant& value) const { value << m_value; return true; } -bool BitmapChoiseRenderer::Render(wxRect rect, wxDC* dc, int state) + +bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) { int xoffset = 0; @@ -1993,69 +2121,25 @@ bool BitmapChoiseRenderer::Render(wxRect rect, wxDC* dc, int state) xoffset = icon.GetWidth() + 4; } + if (rect.height==0) + rect.height= icon.GetHeight(); RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; } -wxSize BitmapChoiseRenderer::GetSize() const +wxSize BitmapChoiceRenderer::GetSize() const { - if (!m_value.GetText().empty()) - { - wxSize size = GetTextExtent(m_value.GetText()); + wxSize sz = GetTextExtent(m_value.GetText()); - if (m_value.GetBitmap().IsOk()) - size.x += m_value.GetBitmap().GetWidth() + 4; - return size; - } - return wxSize(80, 20); - - /* from wxDataViewChoiceRenderer - wxSize sz; - - for ( wxArrayString::const_iterator i = m_choices.begin(); i != m_choices.end(); ++i ) - sz.IncTo(GetTextExtent(*i)); - - // Allow some space for the right-side button, which is approximately the - // size of a scrollbar (and getting pixel-exact value would be complicated). - // Also add some whitespace between the text and the button: - sz.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); - sz.x += GetTextExtent("M").x; + if (m_value.GetBitmap().IsOk()) + sz.x += m_value.GetBitmap().GetWidth() + 4; return sz; - */ -} - -static void update_extruder_color_icons_in_cache() -{ - // Create the bitmap with color bars. - std::vector bmps; - std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - unsigned char rgb[3]; - - /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const double em = Slic3r::GUI::wxGetApp().em_unit(); - const int icon_width = lround(3.6 * em); - const int icon_height = lround(1.6 * em); - - for (const std::string& color : colors) - { - wxBitmap* bitmap = m_bitmap_cache->find(color); - if (bitmap == nullptr) { - // Paint the color icon. - Slic3r::PresetBundle::parse_color(color, rgb); - bitmap = m_bitmap_cache->insert(color, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); - } - bmps.emplace_back(bitmap); - } } -wxWindow* BitmapChoiseRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) { wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); @@ -2063,28 +2147,36 @@ wxWindow* BitmapChoiseRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject))) return nullptr; + std::vector icons = get_extruder_color_icons(); + if (icons.empty()) + return nullptr; + DataViewBitmapText data; data << value; -// m_was_unusable_symbol = false; - - wxPoint position = labelRect.GetPosition(); - if (data.GetBitmap().IsOk()) { - const int bmp_width = data.GetBitmap().GetWidth(); - position.x += bmp_width; - labelRect.SetWidth(labelRect.GetWidth() - bmp_width); - } - - auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, //data.GetText(), + auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), 0, nullptr , wxCB_READONLY); - c_editor->Move(labelRect.GetRight() - c_editor->GetRect().width, wxDefaultCoord); - c_editor->SetStringSelection(data.GetText()); + int i=0; + for (wxBitmap* bmp : icons) { + if (i==0) { + c_editor->Append(_(L("default")), *bmp); + ++i; + } + + c_editor->Append(wxString::Format("%d", i), *bmp); + ++i; + } + c_editor->SetSelection(atoi(data.GetText().c_str())); + + // to avoid event propagation to other sidebar items + c_editor->Bind(wxEVT_COMBOBOX, [](wxCommandEvent& evt) { evt.StopPropagation(); }); + return c_editor; } -bool BitmapChoiseRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) { wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; int selection = c->GetSelection(); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 3d90e966c..762fd08e7 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -209,6 +209,7 @@ class ObjectDataViewModelNode int m_idx = -1; bool m_container = false; wxString m_extruder = "default"; + wxBitmap m_extruder_bmp; wxBitmap m_action_icon; PrintIndicator m_printable {piUndef}; wxBitmap m_printable_icon; @@ -224,7 +225,7 @@ public: m_type(itObject), m_extruder(extruder) { - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -240,7 +241,7 @@ public: m_extruder (extruder) { m_bmp = bmp; - set_action_icon(); + set_action_and_extruder_icons(); init_container(); } @@ -356,7 +357,7 @@ public: } // Set action icons for node - void set_action_icon(); + void set_action_and_extruder_icons(); // Set printable icon for node void set_printable_icon(PrintIndicator printable); @@ -438,6 +439,8 @@ public: wxString GetName(const wxDataViewItem &item) const; wxBitmap& GetBitmap(const wxDataViewItem &item) const; + wxString GetExtruder(const wxDataViewItem &item) const; + int GetExtruderNumber(const wxDataViewItem &item) const; // helper methods to change the model @@ -454,6 +457,8 @@ public: const int item_idx, unsigned int col); + void SetExtruder(const wxString& extruder, wxDataViewItem item); + // For parent move child from cur_volume_id place to new_volume_id // Remaining items will moved up/down accordingly wxDataViewItem ReorganizeChildren( const int cur_volume_id, @@ -504,6 +509,9 @@ public: void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; + bool UpdateColumValues(unsigned col); + void UpdateExtruderBitmap(wxDataViewItem item); + private: wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type); wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); @@ -564,36 +572,31 @@ private: // ---------------------------------------------------------------------------- -// BitmapChoiseRenderer +// BitmapChoiceRenderer // ---------------------------------------------------------------------------- -class BitmapChoiseRenderer : public wxDataViewCustomRenderer +class BitmapChoiceRenderer : public wxDataViewCustomRenderer { public: - BitmapChoiseRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT - - , int align = wxDVR_DEFAULT_ALIGNMENT + BitmapChoiceRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, + int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} bool SetValue(const wxVariant& value); bool GetValue(wxVariant& value) const; -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY - virtual wxString GetAccessibleDescription() const override; -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING virtual bool Render(wxRect cell, wxDC* dc, int state); virtual wxSize GetSize() const; bool HasEditorCtrl() const override { return true; } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl(wxWindow* ctrl, - wxVariant& value) override; + wxWindow* CreateEditorCtrl(wxWindow* parent, + wxRect labelRect, + const wxVariant& value) override; + bool GetValueFromEditorCtrl( wxWindow* ctrl, + wxVariant& value) override; private: DataViewBitmapText m_value; - wxArrayString m_choices; }; From fbe38fb2a44fd43d96d0f4b55b1aeac08e999ca1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Oct 2019 10:57:07 +0200 Subject: [PATCH 08/23] #3008 - Generate all mipmaps up to size 1x1 --- src/slic3r/GUI/GLTexture.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 55ca5f723..62129cfc0 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -107,8 +107,8 @@ void GLTexture::Compressor::compress() break; // stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4, - // crashes if doing so, so we start with twice the required size - level.compressed_data = std::vector(level.w * level.h * 2, 0); + // crashes if doing so, requiring a minimum of 16 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size + level.compressed_data = std::vector(std::max((unsigned int)16, level.w * level.h * 2), 0); int compressed_size = 0; rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size); level.compressed_data.resize(compressed_size); @@ -455,8 +455,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo int lod_w = m_width; int lod_h = m_height; GLint level = 0; - // we do not need to generate all levels down to 1x1 - while ((lod_w > 16) || (lod_h > 16)) + while ((lod_w > 1) || (lod_h > 1)) { ++level; @@ -600,8 +599,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo int lod_w = m_width; int lod_h = m_height; GLint level = 0; - // we do not need to generate all levels down to 1x1 - while ((lod_w > 16) || (lod_h > 16)) + while ((lod_w > 1) || (lod_h > 1)) { ++level; From 15902766d051315d8efe7a5e2771b28af45a69ba Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 11:53:50 +0200 Subject: [PATCH 09/23] Workaround for extruder editing under OSX --- src/slic3r/GUI/GUI_ObjectList.cpp | 71 +++++++++++++++++++++++++++++++ src/slic3r/GUI/GUI_ObjectList.hpp | 3 ++ src/slic3r/GUI/wxExtensions.cpp | 2 +- src/slic3r/GUI/wxExtensions.hpp | 2 + 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a59156fff..3e0d6dd8f 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -788,6 +788,9 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) const wxPoint pt = get_mouse_position_in_control(); HitTest(pt, item, col); + if (m_extruder_editor) + m_extruder_editor->Hide(); + /* Note: Under OSX right click doesn't send "selection changed" event. * It means that Selection() will be return still previously selected item. * Thus under OSX we should force UnselectAll(), when item and col are nullptr, @@ -836,6 +839,8 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) fix_through_netfabb(); } } + else if (/*wxOSX &&*/evt_context_menu && title == _("Extruder")) + extruder_editing(); #ifndef __WXMSW__ GetMainWindow()->SetToolTip(""); // hide tooltip @@ -877,6 +882,72 @@ void ObjectList::show_context_menu(const bool evt_context_menu) wxGetApp().plater()->PopupMenu(menu); } +void ObjectList::extruder_editing() +{ + wxDataViewItem item = GetSelection(); + if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) + return; + + std::vector icons = get_extruder_color_icons(); + if (icons.empty()) + return; + + const int column_width = GetColumn(colExtruder)->GetWidth(); + + wxPoint pos = get_mouse_position_in_control(); + + pos.y -= 2*GetTextExtent("m").y; + + wxWindow* parent = this;//this->GetMainWindow(); + + if (!m_extruder_editor) + m_extruder_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, + pos, wxSize(column_width, -1), + 0, nullptr, wxCB_READONLY); + else + { + m_extruder_editor->SetPosition(pos); + m_extruder_editor->Clear(); + m_extruder_editor->Show(); + } + + + int i = 0; + for (wxBitmap* bmp : icons) { + if (i == 0) { + m_extruder_editor->Append(_(L("default")), *bmp); + ++i; + } + + m_extruder_editor->Append(wxString::Format("%d", i), *bmp); + ++i; + } + m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item)); + + auto set_extruder = [this, item]() + { + const int selection = m_extruder_editor->GetSelection(); + if (selection >= 0) + m_objects_model->SetExtruder(m_extruder_editor->GetString(selection), item); + + m_extruder_editor->Hide(); + }; + + // to avoid event propagation to other sidebar items + m_extruder_editor->Bind(wxEVT_COMBOBOX, [set_extruder](wxCommandEvent& evt) + { + set_extruder(); + evt.StopPropagation(); + }); + + m_extruder_editor->Bind(wxEVT_KILL_FOCUS, [set_extruder](wxFocusEvent& evt) + { + set_extruder(); + evt.Skip(); + }); + +} + void ObjectList::copy() { // if (m_selection_mode & smLayer) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b89d8943d..ddbbd7146 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -140,6 +140,8 @@ private: DynamicPrintConfig *m_config {nullptr}; std::vector *m_objects{ nullptr }; + wxBitmapComboBox *m_extruder_editor { nullptr }; + std::vector m_bmp_vector; t_layer_config_ranges m_layer_config_ranges_cache; @@ -210,6 +212,7 @@ public: void selection_changed(); void show_context_menu(const bool evt_context_menu); + void extruder_editing(); #ifndef __WXOSX__ void key_event(wxKeyEvent& event); #endif /* __WXOSX__ */ diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index b692d1cae..855e803e3 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -449,7 +449,7 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; -static std::vector get_extruder_color_icons() +/*static*/ std::vector get_extruder_color_icons() { // Create the bitmap with color bars. std::vector bmps; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 762fd08e7..1ba55c4b5 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -55,6 +55,8 @@ int em_unit(wxWindow* win); wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); +std::vector get_extruder_color_icons(); + class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { static const unsigned int DefaultWidth; From d87f2d11ae05855cd69698f1f4833e1994c84006 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 12:17:02 +0200 Subject: [PATCH 10/23] Fix OSX build --- src/slic3r/GUI/GUI_ObjectList.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index ddbbd7146..87be25ed9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -12,6 +12,7 @@ #include "wxExtensions.hpp" class wxBoxSizer; +class wxBitmapComboBox; class wxMenuItem; class ObjectDataViewModel; class MenuWithSeparators; From fa81b961c50d5d22cacb5a61265a3c51296bcdc6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Oct 2019 13:56:41 +0200 Subject: [PATCH 11/23] Fix compilation without pch. --- src/libslic3r/Utils.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 5d847573d..9af2adcc6 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "libslic3r.h" From 4171a6a80d9f662537e4f955f7a55ee8d7e542a1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 13:05:34 +0200 Subject: [PATCH 12/23] Improvements for https://github.com/prusa3d/PrusaSlicer/commit/15902766d051315d8efe7a5e2771b28af45a69ba --- src/slic3r/GUI/GUI_ObjectList.cpp | 14 ++++++-------- src/slic3r/GUI/wxExtensions.hpp | 9 +++++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3e0d6dd8f..f8e708200 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -839,7 +839,8 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/) fix_through_netfabb(); } } - else if (/*wxOSX &&*/evt_context_menu && title == _("Extruder")) + // workaround for extruder editing under OSX + else if (wxOSX && evt_context_menu && title == _("Extruder")) extruder_editing(); #ifndef __WXMSW__ @@ -895,15 +896,12 @@ void ObjectList::extruder_editing() const int column_width = GetColumn(colExtruder)->GetWidth(); wxPoint pos = get_mouse_position_in_control(); - - pos.y -= 2*GetTextExtent("m").y; - - wxWindow* parent = this;//this->GetMainWindow(); + pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth(); +// pos.y -= 2*GetTextExtent("m").y; if (!m_extruder_editor) - m_extruder_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, - pos, wxSize(column_width, -1), - 0, nullptr, wxCB_READONLY); + m_extruder_editor = new wxBitmapComboBox(this, wxID_ANY, wxEmptyString, pos, wxSize(column_width, -1), + 0, nullptr, wxCB_READONLY); else { m_extruder_editor->SetPosition(pos); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 1ba55c4b5..d4d5e7998 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -580,8 +580,13 @@ private: class BitmapChoiceRenderer : public wxDataViewCustomRenderer { public: - BitmapChoiceRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE, - int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL + BitmapChoiceRenderer(wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + ,int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} bool SetValue(const wxVariant& value); From f29e18dad2eff92f29b75e505174453e05139f81 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Oct 2019 14:25:32 +0200 Subject: [PATCH 13/23] Fix crashing test executable on gcc 4.9 --- src/libslic3r/Config.cpp | 2 -- src/libslic3r/Config.hpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 9aab3a0eb..63bd0c801 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -271,8 +271,6 @@ ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, Con return def; } -std::string ConfigOptionDef::nocli = "~~~noCLI"; - std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const { // prepare a function for wrapping text diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 334593ab5..463f6ef2d 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1444,7 +1444,7 @@ public: std::vector cli_args(const std::string &key) const; // Assign this key to cli to disable CLI for this option. - static std::string nocli; + static const constexpr char *nocli = "~~~noCLI"; }; // Map from a config option name to its definition. From d5dcba00b1081cf15324d3d5eb8eae4a4701386c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 24 Sep 2019 10:48:24 +0200 Subject: [PATCH 14/23] Time conversion functions with tests. Fixes issue with incorrect characters in time strings on UI. Fix platform dependency Fix return value with incorrect strings. Just use strptime and strftime on all platforms. Emulate strptime on msvc... because they don't have it and their get_time is buggy. --- CMakeLists.txt | 2 +- src/libslic3r/SLA/SLARasterWriter.cpp | 2 +- src/libslic3r/Time.cpp | 254 +++++++++++++++++------ src/libslic3r/Time.hpp | 62 ++++-- src/libslic3r/utils.cpp | 2 +- src/slic3r/Config/Snapshot.cpp | 6 +- src/slic3r/GUI/ConfigSnapshotDialog.cpp | 9 +- tests/CMakeLists.txt | 12 +- tests/timeutils/CMakeLists.txt | 5 + tests/timeutils/timeutils_tests_main.cpp | 41 ++++ 10 files changed, 296 insertions(+), 99 deletions(-) create mode 100644 tests/timeutils/CMakeLists.txt create mode 100644 tests/timeutils/timeutils_tests_main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c7fc12df..93339eb0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux") # Proposal for C++ unit tests and sandboxes option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF) -option(SLIC3R_BUILD_TESTS "Build unit tests" OFF) +option(SLIC3R_BUILD_TESTS "Build unit tests" ON) # Print out the SLIC3R_* cache options get_cmake_property(_cache_vars CACHE_VARIABLES) diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp index 3e6f015d4..425f4c3c2 100644 --- a/src/libslic3r/SLA/SLARasterWriter.cpp +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -114,7 +114,7 @@ void SLARasterWriter::set_config(const DynamicPrintConfig &cfg) m_config["printerProfile"] = get_cfg_value(cfg, "printer_settings_id"); m_config["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id"); - m_config["fileCreationTimestamp"] = Utils::current_utc_time2str(); + m_config["fileCreationTimestamp"] = Utils::utc_timestamp(); m_config["prusaSlicerVersion"] = SLIC3R_BUILD_ID; } diff --git a/src/libslic3r/Time.cpp b/src/libslic3r/Time.cpp index 1f65189b8..063dbb41c 100644 --- a/src/libslic3r/Time.cpp +++ b/src/libslic3r/Time.cpp @@ -3,116 +3,232 @@ #include #include #include +#include +#include +#include -//#include -//#include +#ifdef _MSC_VER +#include +#endif - -#ifdef WIN32 - #define WIN32_LEAN_AND_MEAN - #include - #undef WIN32_LEAN_AND_MEAN -#endif /* WIN32 */ +#include "libslic3r/Utils.hpp" namespace Slic3r { namespace Utils { -namespace { +// "YYYY-MM-DD at HH:MM::SS [UTC]" +// If TimeZone::utc is used with the conversion functions, it will append the +// UTC letters to the end. +static const constexpr char *const SLICER_UTC_TIME_FMT = "%Y-%m-%d at %T"; -// FIXME: after we switch to gcc > 4.9 on the build server, please remove me -#if defined(__GNUC__) && __GNUC__ <= 4 -std::string put_time(const std::tm *tm, const char *fmt) +// ISO8601Z representation of time, without time zone info +static const constexpr char *const ISO8601Z_TIME_FMT = "%Y%m%dT%H%M%SZ"; + +static const char * get_fmtstr(TimeFormat fmt) { - static const constexpr int MAX_CHARS = 200; - char out[MAX_CHARS]; - std::strftime(out, MAX_CHARS, fmt, tm); - return out; + switch (fmt) { + case TimeFormat::gcode: return SLICER_UTC_TIME_FMT; + case TimeFormat::iso8601Z: return ISO8601Z_TIME_FMT; + } + + return ""; } -#else -auto put_time(const std::tm *tm, const char *fmt) -> decltype (std::put_time(tm, fmt)) + +namespace __get_put_time_emulation { +// FIXME: Implementations with the cpp11 put_time and get_time either not +// compile or do not pass the tests on the build server. If we switch to newer +// compilers, this namespace can be deleted with all its content. + +#ifdef _MSC_VER +// VS2019 implementation fails with ISO8601Z_TIME_FMT. +// VS2019 does not have std::strptime either. See bug: +// https://developercommunity.visualstudio.com/content/problem/140618/c-stdget-time-not-parsing-correctly.html + +static const std::map sscanf_fmt_map = { + {SLICER_UTC_TIME_FMT, "%04d-%02d-%02d at %02d:%02d:%02d"}, + {std::string(SLICER_UTC_TIME_FMT) + " UTC", "%04d-%02d-%02d at %02d:%02d:%02d UTC"}, + {ISO8601Z_TIME_FMT, "%04d%02d%02dT%02d%02d%02dZ"} +}; + +static const char * strptime(const char *str, const char *const fmt, std::tm *tms) { - return std::put_time(tm, fmt); + auto it = sscanf_fmt_map.find(fmt); + if (it == sscanf_fmt_map.end()) return nullptr; + + int y, M, d, h, m, s; + if (sscanf(str, it->second.c_str(), &y, &M, &d, &h, &m, &s) != 6) + return nullptr; + + tms->tm_year = y - 1900; // Year since 1900 + tms->tm_mon = M - 1; // 0-11 + tms->tm_mday = d; // 1-31 + tms->tm_hour = h; // 0-23 + tms->tm_min = m; // 0-59 + tms->tm_sec = s; // 0-61 (0-60 in C++11) + + return str; // WARN strptime return val should point after the parsed string } #endif +template +struct GetPutTimeReturnT { + Ttm *tms; + const char *fmt; + GetPutTimeReturnT(Ttm *_tms, const char *_fmt): tms(_tms), fmt(_fmt) {} +}; + +using GetTimeReturnT = GetPutTimeReturnT; +using PutTimeReturnT = GetPutTimeReturnT; + +std::ostream &operator<<(std::ostream &stream, PutTimeReturnT &&pt) +{ + static const constexpr int MAX_CHARS = 200; + char _out[MAX_CHARS]; + strftime(_out, MAX_CHARS, pt.fmt, pt.tms); + stream << _out; + return stream; } -time_t parse_time_ISO8601Z(const std::string &sdate) +inline PutTimeReturnT put_time(const std::tm *tms, const char *fmt) { - int y, M, d, h, m, s; - if (sscanf(sdate.c_str(), "%04d%02d%02dT%02d%02d%02dZ", &y, &M, &d, &h, &m, &s) != 6) - return time_t(-1); - struct tm tms; - tms.tm_year = y - 1900; // Year since 1900 - tms.tm_mon = M - 1; // 0-11 - tms.tm_mday = d; // 1-31 - tms.tm_hour = h; // 0-23 - tms.tm_min = m; // 0-59 - tms.tm_sec = s; // 0-61 (0-60 in C++11) + return {tms, fmt}; +} + +std::istream &operator>>(std::istream &stream, GetTimeReturnT &>) +{ + std::string line; + std::getline(stream, line); + + if (strptime(line.c_str(), gt.fmt, gt.tms) == nullptr) + stream.setstate(std::ios::failbit); + + return stream; +} + +inline GetTimeReturnT get_time(std::tm *tms, const char *fmt) +{ + return {tms, fmt}; +} + +} + +namespace { + +// Platform independent versions of gmtime and localtime. Completely thread +// safe only on Linux. MSVC gtime_s and localtime_s sets global errno thus not +// thread safe. +struct std::tm * _gmtime_r(const time_t *timep, struct tm *result) +{ + assert(timep != nullptr && result != nullptr); #ifdef WIN32 - return _mkgmtime(&tms); + time_t t = *timep; + gmtime_s(result, &t); + return result; +#else + return gmtime_r(timep, result); +#endif +} + +struct std::tm * _localtime_r(const time_t *timep, struct tm *result) +{ + assert(timep != nullptr && result != nullptr); +#ifdef WIN32 + // Converts a time_t time value to a tm structure, and corrects for the + // local time zone. + time_t t = *timep; + localtime_s(result, &t); + return result; +#else + return localtime_r(timep, result); +#endif +} + +time_t _mktime(const struct std::tm *tms) +{ + assert(tms != nullptr); + std::tm _tms = *tms; + return mktime(&_tms); +} + +time_t _timegm(const struct std::tm *tms) +{ + std::tm _tms = *tms; +#ifdef WIN32 + return _mkgmtime(&_tms); #else /* WIN32 */ - return timegm(&tms); + return timegm(&_tms); #endif /* WIN32 */ } -std::string format_time_ISO8601Z(time_t time) +std::string process_format(const char *fmt, TimeZone zone) { - struct tm tms; -#ifdef WIN32 - gmtime_s(&tms, &time); -#else - gmtime_r(&time, &tms); -#endif - char buf[128]; - sprintf(buf, "%04d%02d%02dT%02d%02d%02dZ", - tms.tm_year + 1900, - tms.tm_mon + 1, - tms.tm_mday, - tms.tm_hour, - tms.tm_min, - tms.tm_sec); - return buf; + std::string fmtstr(fmt); + + if (fmtstr == SLICER_UTC_TIME_FMT && zone == TimeZone::utc) + fmtstr += " UTC"; + + return fmtstr; } -std::string format_local_date_time(time_t time) -{ - struct tm tms; -#ifdef WIN32 - // Converts a time_t time value to a tm structure, and corrects for the local time zone. - localtime_s(&tms, &time); -#else - localtime_r(&time, &tms); -#endif - char buf[80]; - strftime(buf, 80, "%x %X", &tms); - return buf; -} +} // namespace time_t get_current_time_utc() -{ +{ using clk = std::chrono::system_clock; return clk::to_time_t(clk::now()); } -static std::string tm2str(const std::tm *tm, const char *fmt) +static std::string tm2str(const std::tm *tms, const char *fmt) { std::stringstream ss; - ss << put_time(tm, fmt); + ss.imbue(std::locale("C")); + ss << __get_put_time_emulation::put_time(tms, fmt); return ss.str(); } -std::string time2str(const time_t &t, TimeZone zone, const char *fmt) +std::string time2str(const time_t &t, TimeZone zone, TimeFormat fmt) { std::string ret; - + std::tm tms = {}; + tms.tm_isdst = -1; + std::string fmtstr = process_format(get_fmtstr(fmt), zone); + switch (zone) { - case TimeZone::local: ret = tm2str(std::localtime(&t), fmt); break; - case TimeZone::utc: ret = tm2str(std::gmtime(&t), fmt) + " UTC"; break; + case TimeZone::local: + ret = tm2str(_localtime_r(&t, &tms), fmtstr.c_str()); break; + case TimeZone::utc: + ret = tm2str(_gmtime_r(&t, &tms), fmtstr.c_str()); break; } - + return ret; } +static time_t str2time(std::istream &stream, TimeZone zone, const char *fmt) +{ + std::tm tms = {}; + tms.tm_isdst = -1; + + stream >> __get_put_time_emulation::get_time(&tms, fmt); + time_t ret = time_t(-1); + + switch (zone) { + case TimeZone::local: ret = _mktime(&tms); break; + case TimeZone::utc: ret = _timegm(&tms); break; + } + + if (stream.fail() || ret < time_t(0)) ret = time_t(-1); + + return ret; +} + +time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt) +{ + std::string fmtstr = process_format(get_fmtstr(fmt), zone).c_str(); + std::stringstream ss(str); + + ss.imbue(std::locale("C")); + return str2time(ss, zone, fmtstr.c_str()); +} + }; // namespace Utils }; // namespace Slic3r diff --git a/src/libslic3r/Time.hpp b/src/libslic3r/Time.hpp index b314e47f7..6b3fd1525 100644 --- a/src/libslic3r/Time.hpp +++ b/src/libslic3r/Time.hpp @@ -7,41 +7,61 @@ namespace Slic3r { namespace Utils { -// Utilities to convert an UTC time_t to/from an ISO8601 time format, -// useful for putting timestamps into file and directory names. -// Returns (time_t)-1 on error. -time_t parse_time_ISO8601Z(const std::string &s); -std::string format_time_ISO8601Z(time_t time); - -// Format the date and time from an UTC time according to the active locales and a local time zone. -// TODO: make sure time2str is a suitable replacement -std::string format_local_date_time(time_t time); - -// There is no gmtime() on windows. +// Should be thread safe. time_t get_current_time_utc(); -const constexpr char *const SLIC3R_TIME_FMT = "%Y-%m-%d at %T"; - enum class TimeZone { local, utc }; +enum class TimeFormat { gcode, iso8601Z }; -std::string time2str(const time_t &t, TimeZone zone, const char *fmt = SLIC3R_TIME_FMT); +// time_t to string functions... -inline std::string current_time2str(TimeZone zone, const char *fmt = SLIC3R_TIME_FMT) +std::string time2str(const time_t &t, TimeZone zone, TimeFormat fmt); + +inline std::string time2str(TimeZone zone, TimeFormat fmt) { return time2str(get_current_time_utc(), zone, fmt); } -inline std::string current_local_time2str(const char * fmt = SLIC3R_TIME_FMT) +inline std::string utc_timestamp(time_t t) { - return current_time2str(TimeZone::local, fmt); + return time2str(t, TimeZone::utc, TimeFormat::gcode); } -inline std::string current_utc_time2str(const char * fmt = SLIC3R_TIME_FMT) +inline std::string utc_timestamp() { - return current_time2str(TimeZone::utc, fmt); + return utc_timestamp(get_current_time_utc()); } -}; // namespace Utils -}; // namespace Slic3r +// String to time_t function. Returns time_t(-1) if fails to parse the input. +time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt); + + +// ///////////////////////////////////////////////////////////////////////////// +// Utilities to convert an UTC time_t to/from an ISO8601 time format, +// useful for putting timestamps into file and directory names. +// Returns (time_t)-1 on error. + +// Use these functions to convert safely to and from the ISO8601 format on +// all platforms + +inline std::string iso_utc_timestamp(time_t t) +{ + return time2str(t, TimeZone::utc, TimeFormat::gcode); +} + +inline std::string iso_utc_timestamp() +{ + return iso_utc_timestamp(get_current_time_utc()); +} + +inline time_t parse_iso_utc_timestamp(const std::string &str) +{ + return str2time(str, TimeZone::utc, TimeFormat::iso8601Z); +} + +// ///////////////////////////////////////////////////////////////////////////// + +} // namespace Utils +} // namespace Slic3r #endif /* slic3r_Utils_Time_hpp_ */ diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 895efdb4d..8d2a6a866 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -543,7 +543,7 @@ std::string string_printf(const char *format, ...) std::string header_slic3r_generated() { - return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::current_utc_time2str(); + return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp(); } unsigned get_current_pid() diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 80b6521b6..7f157fb1a 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -66,7 +66,7 @@ void Snapshot::load_ini(const std::string &path) if (kvp.first == "id") this->id = kvp.second.data(); else if (kvp.first == "time_captured") { - this->time_captured = Slic3r::Utils::parse_time_ISO8601Z(kvp.second.data()); + this->time_captured = Slic3r::Utils::parse_iso_utc_timestamp(kvp.second.data()); if (this->time_captured == (time_t)-1) throw_on_parse_error("invalid timestamp"); } else if (kvp.first == "slic3r_version_captured") { @@ -165,7 +165,7 @@ void Snapshot::save_ini(const std::string &path) // Export the common "snapshot". c << std::endl << "[snapshot]" << std::endl; c << "id = " << this->id << std::endl; - c << "time_captured = " << Slic3r::Utils::format_time_ISO8601Z(this->time_captured) << std::endl; + c << "time_captured = " << Slic3r::Utils::iso_utc_timestamp(this->time_captured) << std::endl; c << "slic3r_version_captured = " << this->slic3r_version_captured.to_string() << std::endl; c << "comment = " << this->comment << std::endl; c << "reason = " << reason_string(this->reason) << std::endl; @@ -365,7 +365,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: Snapshot snapshot; // Snapshot header. snapshot.time_captured = Slic3r::Utils::get_current_time_utc(); - snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured); + snapshot.id = Slic3r::Utils::iso_utc_timestamp(snapshot.time_captured); snapshot.slic3r_version_captured = Slic3r::SEMVER; snapshot.comment = comment; snapshot.reason = reason; diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index c89e4895e..d48dfccc9 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -35,9 +35,14 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5"); text += "\">"; text += ""; + + static const constexpr char *LOCALE_TIME_FMT = "%x %X"; + wxString datetime = wxDateTime(snapshot.time_captured).Format(LOCALE_TIME_FMT); + // Format the row header. - text += wxString("") + (snapshot_active ? _(L("Active")) + ": " : "") + - Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason); + text += wxString("") + (snapshot_active ? _(L("Active")) + ": " : "") + + datetime + ": " + format_reason(snapshot.reason); + if (! snapshot.comment.empty()) text += " (" + wxString::FromUTF8(snapshot.comment.data()) + ")"; text += "
"; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11bdc4b3d..1246c3043 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,13 @@ # TODO Add individual tests as executables in separate directories +# add_subirectory() -# add_subirectory() \ No newline at end of file +find_package(GTest REQUIRED) + +set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) +file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR) + +add_library(test_common INTERFACE) +target_compile_definitions(test_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}") +target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main) + +add_subdirectory(timeutils) diff --git a/tests/timeutils/CMakeLists.txt b/tests/timeutils/CMakeLists.txt new file mode 100644 index 000000000..75048baad --- /dev/null +++ b/tests/timeutils/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(timeutils_tests timeutils_tests_main.cpp) +target_link_libraries(timeutils_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}) + +add_test(timeutils_tests timeutils_tests) +#gtest_discover_tests(timeutils_tests TEST_PREFIX timeutils.) diff --git a/tests/timeutils/timeutils_tests_main.cpp b/tests/timeutils/timeutils_tests_main.cpp new file mode 100644 index 000000000..2a8499270 --- /dev/null +++ b/tests/timeutils/timeutils_tests_main.cpp @@ -0,0 +1,41 @@ +#include +#include "libslic3r/Time.hpp" + +#include +#include +#include + +namespace { + +void test_time_fmt(Slic3r::Utils::TimeFormat fmt) { + using namespace Slic3r::Utils; + time_t t = get_current_time_utc(); + + std::string tstr = time2str(t, TimeZone::local, fmt); + time_t parsedtime = str2time(tstr, TimeZone::local, fmt); + ASSERT_EQ(t, parsedtime); + + tstr = time2str(t, TimeZone::utc, fmt); + parsedtime = str2time(tstr, TimeZone::utc, fmt); + ASSERT_EQ(t, parsedtime); + + parsedtime = str2time("not valid string", TimeZone::local, fmt); + ASSERT_EQ(parsedtime, time_t(-1)); + + parsedtime = str2time("not valid string", TimeZone::utc, fmt); + ASSERT_EQ(parsedtime, time_t(-1)); +} +} + +TEST(Timeutils, ISO8601Z) { + test_time_fmt(Slic3r::Utils::TimeFormat::iso8601Z); +} + +TEST(Timeutils, Slic3r_UTC_Time_Format) { + test_time_fmt(Slic3r::Utils::TimeFormat::gcode); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 9e01740db66a822d4f9e90c39037649bd96e1093 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 15:02:13 +0200 Subject: [PATCH 15/23] More improvements --- src/slic3r/GUI/GUI_ObjectList.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f8e708200..88cf24509 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -896,20 +896,22 @@ void ObjectList::extruder_editing() const int column_width = GetColumn(colExtruder)->GetWidth(); wxPoint pos = get_mouse_position_in_control(); + wxSize size = wxSize(column_width, -1); pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth(); -// pos.y -= 2*GetTextExtent("m").y; + pos.y -= GetTextExtent("m").y; if (!m_extruder_editor) - m_extruder_editor = new wxBitmapComboBox(this, wxID_ANY, wxEmptyString, pos, wxSize(column_width, -1), + m_extruder_editor = new wxBitmapComboBox(this, wxID_ANY, wxEmptyString, pos, size, 0, nullptr, wxCB_READONLY); else { m_extruder_editor->SetPosition(pos); + m_extruder_editor->SetMinSize(size); + m_extruder_editor->SetSize(size); m_extruder_editor->Clear(); m_extruder_editor->Show(); } - int i = 0; for (wxBitmap* bmp : icons) { if (i == 0) { @@ -922,8 +924,11 @@ void ObjectList::extruder_editing() } m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item)); - auto set_extruder = [this, item]() + auto set_extruder = [this]() { + wxDataViewItem item = GetSelection(); + if (!item) return; + const int selection = m_extruder_editor->GetSelection(); if (selection >= 0) m_objects_model->SetExtruder(m_extruder_editor->GetString(selection), item); @@ -937,12 +942,12 @@ void ObjectList::extruder_editing() set_extruder(); evt.StopPropagation(); }); - + /* m_extruder_editor->Bind(wxEVT_KILL_FOCUS, [set_extruder](wxFocusEvent& evt) { set_extruder(); evt.Skip(); - }); + });*/ } From 8bc569284e35dd5a5a6d07e7e004af7e980123f9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 16:14:40 +0200 Subject: [PATCH 16/23] Corrected editor position and size --- src/slic3r/GUI/GUI_ObjectList.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 88cf24509..308d0672d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -893,11 +893,11 @@ void ObjectList::extruder_editing() if (icons.empty()) return; - const int column_width = GetColumn(colExtruder)->GetWidth(); + const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; wxPoint pos = get_mouse_position_in_control(); wxSize size = wxSize(column_width, -1); - pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth(); + pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; pos.y -= GetTextExtent("m").y; if (!m_extruder_editor) @@ -2938,6 +2938,7 @@ int ObjectList::get_selected_layers_range_idx() const void ObjectList::update_selections() { + if (m_extruder_editor) m_extruder_editor->Hide(); const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; From b3603a09fcc04dd9dda26824574c0fa0399c594c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Oct 2019 22:50:55 +0200 Subject: [PATCH 17/23] Fixed a bug: Wrong save of a preset if the preset is already exist. Step to repro: 1. Select system "Some_preset" preset 2. Make some changes 3. Save preset with "Some_preset - Copy" name. => "Some_preset - Copy" is selected, there is/are marked option(s) only different from system preset. repeate 1-3 steps => "Some_preset - Copy" is selected, there is/are marked option(s) different from DEFAULT preset. --- src/slic3r/GUI/Preset.cpp | 15 +++++++++++++++ src/slic3r/GUI/Preset.hpp | 3 +++ src/slic3r/GUI/Tab.cpp | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d2503d349..41aaa80f1 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -819,6 +819,21 @@ bool PresetCollection::delete_current_preset() return true; } +bool PresetCollection::delete_preset(const std::string& name) +{ + auto it = this->find_preset_internal(name); + + const Preset& preset = *it; + if (preset.is_default) + return false; + if (!preset.is_external && !preset.is_system) { + // Erase the preset file. + boost::nowide::remove(preset.file.c_str()); + } + m_presets.erase(it); + return true; +} + void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name) { // XXX: See note in PresetBundle::load_compatible_bitmaps() diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index e1efdc1ef..056b03700 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -276,6 +276,9 @@ public: // Delete the current preset, activate the first visible preset. // returns true if the preset was deleted successfully. bool delete_current_preset(); + // Delete the current preset, activate the first visible preset. + // returns true if the preset was deleted successfully. + bool delete_preset(const std::string& name); // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. void load_bitmap_default(wxWindow *window, const std::string &file_name); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d3f331442..afb378510 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3019,6 +3019,18 @@ void Tab::save_preset(std::string name /*= ""*/) show_error(this, _(L("Cannot overwrite an external profile."))); return; } + if (existing && name != preset.name) + { + wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exist."))) % name).str()); + msg_text += "\n" + _(L("Replace?")); + wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + + if (dialog.ShowModal() == wxID_NO) + return; + + // Remove the preset from the list. + m_presets->delete_preset(name); + } } // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini From fe7f2e4d95155f6f79a194f52fd1b8e2af0c50eb Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 4 Oct 2019 11:04:26 +0200 Subject: [PATCH 18/23] Catch2 test framework integration. --- cmake/modules/Catch2/Catch.cmake | 175 + cmake/modules/Catch2/CatchAddTests.cmake | 106 + .../Catch2/ParseAndAddCatchTests.cmake | 225 + tests/CMakeLists.txt | 49 +- tests/catch2/LICENSE.txt | 23 + tests/catch2/VERSION.txt | 2 + tests/catch2/catch.hpp | 17075 ++++++++++++++++ tests/catch2/catch_reporter_automake.hpp | 62 + tests/catch2/catch_reporter_tap.hpp | 253 + tests/catch2/catch_reporter_teamcity.hpp | 220 + tests/example/CMakeLists.txt | 5 + tests/example/example_tests_main.cpp | 6 + tests/timeutils/CMakeLists.txt | 3 +- tests/timeutils/timeutils_tests_main.cpp | 21 +- 14 files changed, 18205 insertions(+), 20 deletions(-) create mode 100644 cmake/modules/Catch2/Catch.cmake create mode 100644 cmake/modules/Catch2/CatchAddTests.cmake create mode 100644 cmake/modules/Catch2/ParseAndAddCatchTests.cmake create mode 100644 tests/catch2/LICENSE.txt create mode 100644 tests/catch2/VERSION.txt create mode 100644 tests/catch2/catch.hpp create mode 100644 tests/catch2/catch_reporter_automake.hpp create mode 100644 tests/catch2/catch_reporter_tap.hpp create mode 100644 tests/catch2/catch_reporter_teamcity.hpp create mode 100644 tests/example/CMakeLists.txt create mode 100644 tests/example/example_tests_main.cpp diff --git a/cmake/modules/Catch2/Catch.cmake b/cmake/modules/Catch2/Catch.cmake new file mode 100644 index 000000000..486e32331 --- /dev/null +++ b/cmake/modules/Catch2/Catch.cmake @@ -0,0 +1,175 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +Catch +----- + +This module defines a function to help use the Catch test framework. + +The :command:`catch_discover_tests` discovers tests by asking the compiled test +executable to enumerate its tests. This does not require CMake to be re-run +when tests change. However, it may not work in a cross-compiling environment, +and setting test properties is less convenient. + +This command is intended to replace use of :command:`add_test` to register +tests, and will create a separate CTest test for each Catch test case. Note +that this is in some cases less efficient, as common set-up and tear-down logic +cannot be shared by multiple test cases executing in the same instance. +However, it provides more fine-grained pass/fail information to CTest, which is +usually considered as more beneficial. By default, the CTest test name is the +same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. + +.. command:: catch_discover_tests + + Automatically add tests with CTest by querying the compiled test executable + for available tests:: + + catch_discover_tests(target + [TEST_SPEC arg1...] + [EXTRA_ARGS arg1...] + [WORKING_DIRECTORY dir] + [TEST_PREFIX prefix] + [TEST_SUFFIX suffix] + [PROPERTIES name1 value1...] + [TEST_LIST var] + ) + + ``catch_discover_tests`` sets up a post-build command on the test executable + that generates the list of tests by parsing the output from running the test + with the ``--list-test-names-only`` argument. This ensures that the full + list of tests is obtained. Since test discovery occurs at build time, it is + not necessary to re-run CMake when the list of tests changes. + However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set + in order to function in a cross-compiling environment. + + Additionally, setting properties on tests is somewhat less convenient, since + the tests are not available at CMake time. Additional test properties may be + assigned to the set of tests as a whole using the ``PROPERTIES`` option. If + more fine-grained test control is needed, custom content may be provided + through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` + directory property. The set of discovered tests is made accessible to such a + script via the ``_TESTS`` variable. + + The options are: + + ``target`` + Specifies the Catch executable, which must be a known CMake executable + target. CMake will substitute the location of the built executable when + running the test. + + ``TEST_SPEC arg1...`` + Specifies test cases, wildcarded test cases, tags and tag expressions to + pass to the Catch executable with the ``--list-test-names-only`` argument. + + ``EXTRA_ARGS arg1...`` + Any extra arguments to pass on the command line to each test case. + + ``WORKING_DIRECTORY dir`` + Specifies the directory in which to run the discovered test cases. If this + option is not provided, the current binary directory is used. + + ``TEST_PREFIX prefix`` + Specifies a ``prefix`` to be prepended to the name of each discovered test + case. This can be useful when the same test executable is being used in + multiple calls to ``catch_discover_tests()`` but with different + ``TEST_SPEC`` or ``EXTRA_ARGS``. + + ``TEST_SUFFIX suffix`` + Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of + every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may + be specified. + + ``PROPERTIES name1 value1...`` + Specifies additional properties to be set on all tests discovered by this + invocation of ``catch_discover_tests``. + + ``TEST_LIST var`` + Make the list of tests available in the variable ``var``, rather than the + default ``_TESTS``. This can be useful when the same test + executable is being used in multiple calls to ``catch_discover_tests()``. + Note that this variable is only available in CTest. + +#]=======================================================================] + +#------------------------------------------------------------------------------ +function(catch_discover_tests TARGET) + cmake_parse_arguments( + "" + "" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST" + "TEST_SPEC;EXTRA_ARGS;PROPERTIES" + ${ARGN} + ) + + if(NOT _WORKING_DIRECTORY) + set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() + if(NOT _TEST_LIST) + set(_TEST_LIST ${TARGET}_TESTS) + endif() + + ## Generate a unique name based on the extra arguments + string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}") + string(SUBSTRING ${args_hash} 0 7 args_hash) + + # Define rule to generate test list for aforementioned test executable + set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") + set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + get_property(crosscompiling_emulator + TARGET ${TARGET} + PROPERTY CROSSCOMPILING_EMULATOR + ) + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "TEST_LIST=${_TEST_LIST}" + -D "CTEST_FILE=${ctest_tests_file}" + -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" + "endif()\n" + ) + + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + else() + # Add discovered tests as directory TEST_INCLUDE_FILE if possible + get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET) + if (NOT ${test_include_file_set}) + set_property(DIRECTORY + PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" + ) + else() + message(FATAL_ERROR + "Cannot set more than one TEST_INCLUDE_FILE" + ) + endif() + endif() + +endfunction() + +############################################################################### + +set(_CATCH_DISCOVER_TESTS_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake +) diff --git a/cmake/modules/Catch2/CatchAddTests.cmake b/cmake/modules/Catch2/CatchAddTests.cmake new file mode 100644 index 000000000..ca5ebc17e --- /dev/null +++ b/cmake/modules/Catch2/CatchAddTests.cmake @@ -0,0 +1,106 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +set(prefix "${TEST_PREFIX}") +set(suffix "${TEST_SUFFIX}") +set(spec ${TEST_SPEC}) +set(extra_args ${TEST_EXTRA_ARGS}) +set(properties ${TEST_PROPERTIES}) +set(script) +set(suite) +set(tests) + +function(add_command NAME) + set(_args "") + foreach(_arg ${ARGN}) + if(_arg MATCHES "[^-./:a-zA-Z0-9_]") + set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument + else() + set(_args "${_args} ${_arg}") + endif() + endforeach() + set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) +endfunction() + +macro(_add_catch_test_labels LINE) + # convert to list of tags + string(REPLACE "][" "]\\;[" tags ${line}) + + add_command( + set_tests_properties "${prefix}${test}${suffix}" + PROPERTIES + LABELS "${tags}" + ) +endmacro() + +macro(_add_catch_test LINE) + set(test ${line}) + # use escape commas to handle properly test cases with commans inside the name + string(REPLACE "," "\\," test_name ${test}) + # ...and add to script + add_command( + add_test "${prefix}${test}${suffix}" + ${TEST_EXECUTOR} + "${TEST_EXECUTABLE}" + "${test_name}" + ${extra_args} + ) + + add_command( + set_tests_properties "${prefix}${test}${suffix}" + PROPERTIES + WORKING_DIRECTORY "${TEST_WORKING_DIR}" + ${properties} + ) + list(APPEND tests "${prefix}${test}${suffix}") +endmacro() + +# Run test executable to get list of available tests +if(NOT EXISTS "${TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable '${TEST_EXECUTABLE}' does not exist" + ) +endif() +execute_process( + COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests + OUTPUT_VARIABLE output + RESULT_VARIABLE result +) +# Catch --list-test-names-only reports the number of tests, so 0 is... surprising +if(${result} EQUAL 0) + message(WARNING + "Test executable '${TEST_EXECUTABLE}' contains no tests!\n" + ) +elseif(${result} LESS 0) + message(FATAL_ERROR + "Error running test executable '${TEST_EXECUTABLE}':\n" + " Result: ${result}\n" + " Output: ${output}\n" + ) +endif() + +string(REPLACE "\n" ";" output "${output}") +set(test) +set(tags_regex "(\\[([^\\[]*)\\])+$") + +# Parse output +foreach(line ${output}) + # lines without leading whitespaces are catch output not tests + if(${line} MATCHES "^[ \t]+") + # strip leading spaces and tabs + string(REGEX REPLACE "^[ \t]+" "" line ${line}) + + if(${line} MATCHES "${tags_regex}") + _add_catch_test_labels(${line}) + else() + _add_catch_test(${line}) + endif() + endif() +endforeach() + +# Create a list of all discovered tests, which users may use to e.g. set +# properties on the tests +add_command(set ${TEST_LIST} ${tests}) + +# Write CTest script +file(WRITE "${CTEST_FILE}" "${script}") diff --git a/cmake/modules/Catch2/ParseAndAddCatchTests.cmake b/cmake/modules/Catch2/ParseAndAddCatchTests.cmake new file mode 100644 index 000000000..925d93281 --- /dev/null +++ b/cmake/modules/Catch2/ParseAndAddCatchTests.cmake @@ -0,0 +1,225 @@ +#==================================================================================================# +# supported macros # +# - TEST_CASE, # +# - SCENARIO, # +# - TEST_CASE_METHOD, # +# - CATCH_TEST_CASE, # +# - CATCH_SCENARIO, # +# - CATCH_TEST_CASE_METHOD. # +# # +# Usage # +# 1. make sure this module is in the path or add this otherwise: # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# 2. make sure that you've enabled testing option for the project by the call: # +# enable_testing() # +# 3. add the lines to the script for testing target (sample CMakeLists.txt): # +# project(testing_target) # +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/") # +# enable_testing() # +# # +# find_path(CATCH_INCLUDE_DIR "catch.hpp") # +# include_directories(${INCLUDE_DIRECTORIES} ${CATCH_INCLUDE_DIR}) # +# # +# file(GLOB SOURCE_FILES "*.cpp") # +# add_executable(${PROJECT_NAME} ${SOURCE_FILES}) # +# # +# include(ParseAndAddCatchTests) # +# ParseAndAddCatchTests(${PROJECT_NAME}) # +# # +# The following variables affect the behavior of the script: # +# # +# PARSE_CATCH_TESTS_VERBOSE (Default OFF) # +# -- enables debug messages # +# PARSE_CATCH_TESTS_NO_HIDDEN_TESTS (Default OFF) # +# -- excludes tests marked with [!hide], [.] or [.foo] tags # +# PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME (Default ON) # +# -- adds fixture class name to the test name # +# PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME (Default ON) # +# -- adds cmake target name to the test name # +# PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # +# -- causes CMake to rerun when file with tests changes so that new tests will be discovered # +# # +# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way # +# a test should be run. For instance to use test MPI, one can write # +# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) # +# just before calling this ParseAndAddCatchTests function # +# # +# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test # +# command. For example, to include successful tests in the output, one can write # +# set(AdditionalCatchParameters --success) # +# # +# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source # +# file in the target is set, and contains the list of the tests extracted from that target, or # +# from that file. This is useful, for example to add further labels or properties to the tests. # +# # +#==================================================================================================# + +if (CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8) + message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer") +endif() + +option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) +option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) +option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) +option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) + +function(ParseAndAddCatchTests_PrintDebugMessage) + if(PARSE_CATCH_TESTS_VERBOSE) + message(STATUS "ParseAndAddCatchTests: ${ARGV}") + endif() +endfunction() + +# This removes the contents between +# - block comments (i.e. /* ... */) +# - full line comments (i.e. // ... ) +# contents have been read into '${CppCode}'. +# !keep partial line comments +function(ParseAndAddCatchTests_RemoveComments CppCode) + string(ASCII 2 CMakeBeginBlockComment) + string(ASCII 3 CMakeEndBlockComment) + string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\\*/" "${CMakeEndBlockComment}" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "${CMakeBeginBlockComment}[^${CMakeEndBlockComment}]*${CMakeEndBlockComment}" "" ${CppCode} "${${CppCode}}") + string(REGEX REPLACE "\n[ \t]*//+[^\n]+" "\n" ${CppCode} "${${CppCode}}") + + set(${CppCode} "${${CppCode}}" PARENT_SCOPE) +endfunction() + +# Worker function +function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) + # If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file. + if(SourceFile MATCHES "\\\$") + ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.") + return() + endif() + # According to CMake docs EXISTS behavior is well-defined only for full paths. + get_filename_component(SourceFile ${SourceFile} ABSOLUTE) + if(NOT EXISTS ${SourceFile}) + message(WARNING "Cannot find source file: ${SourceFile}") + return() + endif() + ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}") + file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) + + # Remove block and fullline comments + ParseAndAddCatchTests_RemoveComments(Contents) + + # Find definition of test names + string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") + + if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) + ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} + ) + endif() + + foreach(TestName ${Tests}) + # Strip newlines + string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") + + # Get test type and fixture if applicable + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") + string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") + string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}") + + # Get string parts of test definition + string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") + + # Strip wrapping quotation marks + string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") + string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") + + # Validate that a test name and tags have been provided + list(LENGTH TestStrings TestStringsLength) + if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) + message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") + endif() + + # Assign name and tags + list(GET TestStrings 0 Name) + if("${TestType}" STREQUAL "SCENARIO") + set(Name "Scenario: ${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture) + set(CTestName "${TestFixture}:${Name}") + else() + set(CTestName "${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) + set(CTestName "${TestTarget}:${CTestName}") + endif() + # add target to labels to enable running all tests added from this target + set(Labels ${TestTarget}) + if(TestStringsLength EQUAL 2) + list(GET TestStrings 1 Tags) + string(TOLOWER "${Tags}" Tags) + # remove target from labels if the test is hidden + if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") + list(REMOVE_ITEM Labels ${TestTarget}) + endif() + string(REPLACE "]" ";" Tags "${Tags}") + string(REPLACE "[" "" Tags "${Tags}") + else() + # unset tags variable from previous loop + unset(Tags) + endif() + + list(APPEND Labels ${Tags}) + + set(HiddenTagFound OFF) + foreach(label ${Labels}) + string(REGEX MATCH "^!hide|^\\." result ${label}) + if(result) + set(HiddenTagFound ON) + break() + endif(result) + endforeach(label) + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") + ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") + else() + ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"") + if(Labels) + ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}") + endif() + + # Escape commas in the test spec + string(REPLACE "," "\\," Name ${Name}) + + # Add the test and set its properties + add_test(NAME "\"${CTestName}\"" COMMAND ${OptionalCatchTestLauncher} $ ${Name} ${AdditionalCatchParameters}) + # Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8") + ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property") + set_tests_properties("\"${CTestName}\"" PROPERTIES DISABLED ON) + else() + set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" + LABELS "${Labels}") + endif() + set_property( + TARGET ${TestTarget} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"") + set_property( + SOURCE ${SourceFile} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "\"${CTestName}\"") + endif() + + + endforeach() +endfunction() + +# entry point +function(ParseAndAddCatchTests TestTarget) + ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}") + get_target_property(SourceFiles ${TestTarget} SOURCES) + ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}") + foreach(SourceFile ${SourceFiles}) + ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget}) + endforeach() + ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}") +endfunction() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1246c3043..9c4a75857 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,13 +1,50 @@ # TODO Add individual tests as executables in separate directories # add_subirectory() -find_package(GTest REQUIRED) - set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR) -add_library(test_common INTERFACE) -target_compile_definitions(test_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}") -target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main) +add_library(Catch2 INTERFACE) +list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2) +target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +add_library(Catch2::Catch2 ALIAS Catch2) +include(Catch) + +add_library(test_catch2_common INTERFACE) +target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}") +target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2) + +add_library(test_common INTERFACE) +target_link_libraries(test_common INTERFACE test_catch2_common) + +# DEPRECATED: +find_package(GTest REQUIRED) +add_library(test_gtest_common INTERFACE) +target_compile_definitions(test_gtest_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}") +target_link_libraries(test_gtest_common INTERFACE GTest::GTest GTest::Main) + +function(prusaslicer_discover_tests TARGET) + catch_discover_tests(${TARGET}) +endfunction() + +macro(subdirlist result curdir) + file(GLOB children RELATIVE ${curdir} ${curdir}/*) + set(dirlist "") + foreach(child ${children}) + if(IS_DIRECTORY ${curdir}/${child} AND NOT ${child} STREQUAL "catch2" ) + list(APPEND dirlist ${child}) + endif() + endforeach() + set(${result} ${dirlist}) +endmacro() + +subdirlist(SUBDIRS ${CMAKE_CURRENT_LIST_DIR}) +message(STATUS ${SUBDIRS}) + +foreach(subdir ${SUBDIRS}) + add_subdirectory(${subdir}) +endforeach() + +#add_subdirectory(timeutils) +#add_subdirectory(example) -add_subdirectory(timeutils) diff --git a/tests/catch2/LICENSE.txt b/tests/catch2/LICENSE.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/tests/catch2/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tests/catch2/VERSION.txt b/tests/catch2/VERSION.txt new file mode 100644 index 000000000..587a8125d --- /dev/null +++ b/tests/catch2/VERSION.txt @@ -0,0 +1,2 @@ +2.9.2 g2c869e1 + diff --git a/tests/catch2/catch.hpp b/tests/catch2/catch.hpp new file mode 100644 index 000000000..5feb2a4be --- /dev/null +++ b/tests/catch2/catch.hpp @@ -0,0 +1,17075 @@ +/* + * Catch v2.9.2 + * Generated: 2019-08-08 13:35:12.279703 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 9 +#define CATCH_VERSION_PATCH 2 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if byte is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_BYTE +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +#include + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + \ + template class L1, typename...E1, template class L2, typename...E2> \ + constexpr auto append(L1, L2) noexcept -> L1 { return {}; }\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + constexpr auto append(L1, L2, Rest...) noexcept -> decltype(append(L1{}, Rest{}...)) { return {}; }\ + template< template class L1, typename...E1, typename...Rest>\ + constexpr auto append(L1, TypeList, Rest...) noexcept -> L1 { return {}; }\ + \ + template< template class Container, template class List, typename...elems>\ + constexpr auto rewrap(List) noexcept -> TypeList> { return {}; }\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + constexpr auto rewrap(List,Elements...) noexcept -> decltype(append(TypeList>{}, rewrap(Elements{}...))) { return {}; }\ + \ + template