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()