diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 379c557b6..ed84135e2 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -4,7 +4,7 @@ use warnings; use utf8; use Slic3r::Print::State ':steps'; -use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE); +use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY); use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX); use base qw(Wx::Panel Class::Accessor); @@ -61,29 +61,24 @@ sub new { my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, "1 Layer"); my $checkbox_color_by_extruder = $self->{checkbox_color_by_extruder} = Wx::CheckBox->new($self, -1, "Tool"); + my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, "View"); + my $choice_view_type = Wx::Choice->new($self, -1); $choice_view_type->Append("Feature type"); $choice_view_type->Append("Height"); $choice_view_type->Append("Width"); $choice_view_type->Append("Speed"); $choice_view_type->SetSelection(0); + + my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, "Show"); - my $checklist_features = Wx::CheckListBox->new($self, -1, wxDefaultPosition, [-1, 150]); - $checklist_features->Append("Perimeter"); - $checklist_features->Append("External perimeter"); - $checklist_features->Append("Overhang perimeter"); - $checklist_features->Append("Internal infill"); - $checklist_features->Append("Solid infill"); - $checklist_features->Append("Top solid infill"); - $checklist_features->Append("Bridge infill"); - $checklist_features->Append("Gap fill"); - $checklist_features->Append("Skirt"); - $checklist_features->Append("Support material"); - $checklist_features->Append("Support material interface"); - for (my $i = 0; $i < $checklist_features->GetCount(); $i += 1) - { - $checklist_features->Check($i, 1); - } + my $combochecklist_features = Wx::ComboCtrl->new(); + $combochecklist_features->Create($self, -1, "Feature types", wxDefaultPosition, [200, -1], wxCB_READONLY); + $combochecklist_features->UseAltPopupWindow(); + $combochecklist_features->EnablePopupAnimation(0); + my $feature_text = "Feature types"; + my $feature_items = "Perimeter|External perimeter|Overhang perimeter|Internal infill|Solid infill|Top solid infill|Bridge infill|Gap fill|Skirt|Support material|Support material interface"; + Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1); my $checkbox_travel = Wx::CheckBox->new($self, -1, "Travel"); my $checkbox_retractions = Wx::CheckBox->new($self, -1, "Retractions"); @@ -102,15 +97,27 @@ sub new { $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); $vsizer_outer->Add($checkbox_color_by_extruder, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); - $vsizer_outer->Add($choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_HORIZONTAL, 5); - $vsizer_outer->Add($checklist_features, 0, wxTOP | wxALL | wxALIGN_CENTER_HORIZONTAL, 5); - $vsizer_outer->Add($checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - $vsizer_outer->Add($checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - $vsizer_outer->Add($checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->Add($choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->AddSpacer(10); + $bottom_sizer->Add($label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->Add($combochecklist_features, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->AddSpacer(20); + $bottom_sizer->Add($checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->AddSpacer(10); + $bottom_sizer->Add($checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + $bottom_sizer->AddSpacer(10); + $bottom_sizer->Add($checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5); + + my $main_sizer = Wx::BoxSizer->new(wxVERTICAL); + $main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0); + $main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0); EVT_SLIDER($self, $slider_low, sub { $slider_high->SetValue($slider_low->GetValue) if $self->single_layer; @@ -194,13 +201,8 @@ sub new { $self->reload_print; $self->auto_zoom(1); }); - EVT_CHECKLISTBOX($self, $checklist_features, sub { - my $flags = 0; - for (my $i = 0; $i < $checklist_features->GetCount(); $i += 1) { - if ($checklist_features->IsChecked($i)) { - $flags += 2 ** $i; - } - } + EVT_CHECKLISTBOX($self, $combochecklist_features, sub { + my $flags = Slic3r::GUI::combochecklist_get_flags($combochecklist_features); $self->print->set_gcode_preview_extrusion_flags($flags); $self->auto_zoom(0); @@ -226,7 +228,7 @@ sub new { $self->auto_zoom(1); }); - $self->SetSizer($sizer); + $self->SetSizer($main_sizer); $self->SetMinSize($self->GetSize); $sizer->SetSizeHints($self); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 9e23112c5..de475eaa0 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -177,6 +177,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/PresetHints.hpp ${LIBDIR}/slic3r/GUI/GUI.cpp ${LIBDIR}/slic3r/GUI/GUI.hpp + ${LIBDIR}/slic3r/GUI/wxExtensions.cpp + ${LIBDIR}/slic3r/GUI/wxExtensions.hpp ) add_library(admesh STATIC diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 8db0508f1..7d107ecbc 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -5,6 +5,9 @@ #include #include +#include +#include + #if __APPLE__ #import #elif _WIN32 @@ -20,6 +23,9 @@ #include #include #include +#include + +#include "slic3r/gui/wxextensions.hpp" namespace Slic3r { namespace GUI { @@ -183,4 +189,46 @@ void create_preset_tab(const char *name) g_wxTabPanel->AddPage(panel, name); } +void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) +{ + wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup(-1); + if (popup != nullptr) + { + comboCtrl->SetPopupControl(popup); + popup->SetStringValue(text); + popup->Connect(-1, wxEVT_CHECKLISTBOX, wxCommandEventHandler(wxCheckListBoxComboPopup::OnCheckListBox), nullptr, popup); + popup->Connect(-1, wxEVT_LISTBOX, wxCommandEventHandler(wxCheckListBoxComboPopup::OnListBoxSelection), nullptr, popup); + + std::vector items_str; + boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off); + + for (const std::string& item : items_str) + { + popup->Append(item); + } + + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + popup->Check(i, initial_value); + } + } +} + +int combochecklist_get_flags(wxComboCtrl* comboCtrl) +{ + int flags = 0; + + wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); + if (popup != nullptr) + { + for (unsigned int i = 0; i < popup->GetCount(); ++i) + { + if (popup->IsChecked(i)) + flags += (int)std::pow(2.0f, (float)i); + } + } + + return flags; +} + } } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 3634e0bc8..f15ba15d6 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -8,6 +8,7 @@ class wxApp; class wxFrame; class wxMenuBar; class wxNotebook; +class wxComboCtrl; namespace Slic3r { namespace GUI { @@ -27,6 +28,16 @@ void add_debug_menu(wxMenuBar *menu); // add it at the end of the tab panel. void create_preset_tab(const char *name); -} } +// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items. +// Items are all initialized to the given value. +// Items must be separated by '|', for example "Item1|Item2|Item3", and so on. +void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value); + +// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, +// encoded inside an int. +int combochecklist_get_flags(wxComboCtrl* comboCtrl); + +} +} #endif diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp new file mode 100644 index 000000000..d8f0434c7 --- /dev/null +++ b/xs/src/slic3r/GUI/wxExtensions.cpp @@ -0,0 +1,74 @@ +#include "wxExtensions.hpp" + +const unsigned int wxCheckListBoxComboPopup::Height = 200; + +wxCheckListBoxComboPopup::wxCheckListBoxComboPopup(wxWindowID id) + : m_id(id) + , m_text(wxEmptyString) +{ +} + +bool wxCheckListBoxComboPopup::Create(wxWindow* parent) +{ + return wxCheckListBox::Create(parent, m_id, wxPoint(0, 0)); +} + +wxWindow* wxCheckListBoxComboPopup::GetControl() +{ + return this; +} + +void wxCheckListBoxComboPopup::SetStringValue(const wxString& value) +{ + m_text = value; +} + +wxString wxCheckListBoxComboPopup::GetStringValue() const +{ + return m_text; +} + +wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) +{ + // matches owner wxComboCtrl's width + + wxComboCtrl* cmb = GetComboCtrl(); + if (cmb != nullptr) + { + wxSize size = GetComboCtrl()->GetSize(); + size.SetHeight(Height); + return size; + } + else + return wxSize(200, Height); +} + +void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt) +{ + // forwards the checklistbox event to the owner wxComboCtrl + + wxComboCtrl* cmb = GetComboCtrl(); + if (cmb != nullptr) + { + wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId()); + cmb->ProcessWindowEvent(event); + } +} + +void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) +{ + // transforms list box item selection event into checklistbox item toggle event + + int selId = GetSelection(); + if (selId != wxNOT_FOUND) + { + Toggle((unsigned int)selId); + SetSelection(wxNOT_FOUND); + + wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId()); + event.SetInt(selId); + event.SetEventObject(this); + event.SetString(GetString(selId)); + ProcessEvent(event); + } +} diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp new file mode 100644 index 000000000..96d59069d --- /dev/null +++ b/xs/src/slic3r/GUI/wxExtensions.hpp @@ -0,0 +1,27 @@ +#ifndef slic3r_GUI_wxExtensions_hpp_ +#define slic3r_GUI_wxExtensions_hpp_ + +#include +#include + +class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup +{ + static const unsigned int Height; + + wxWindowID m_id; + wxString m_text; + +public: + explicit wxCheckListBoxComboPopup(wxWindowID id); + + virtual bool Create(wxWindow* parent); + virtual wxWindow* GetControl(); + virtual void SetStringValue(const wxString& value); + virtual wxString GetStringValue() const; + virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); + + void OnCheckListBox(wxCommandEvent& evt); + void OnListBoxSelection(wxCommandEvent& evt); +}; + +#endif // slic3r_GUI_wxExtensions_hpp_ diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index d6b55dbf1..3b2c47e26 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -37,3 +37,10 @@ void add_debug_menu(SV *ui) void create_preset_tab(const char *name) %code%{ Slic3r::GUI::create_preset_tab(name); %}; + +void create_combochecklist(SV *ui, std::string text, std::string items, bool initial_value) + %code%{ Slic3r::GUI::create_combochecklist((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl"), text, items, initial_value); %}; + +int combochecklist_get_flags(SV *ui) + %code%{ RETVAL=Slic3r::GUI::combochecklist_get_flags((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl")); %}; + \ No newline at end of file