diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7a3b30a6b0..1aa6ba955f 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -117,6 +117,8 @@ sub OnInit { } eval { $self->{preset_bundle}->load_selections($self->{app_config}) }; $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only; + + Slic3r::GUI::set_preset_bundle($self->{preset_bundle}); # application frame Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new); diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index f9e9e668da..db05860da0 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -400,6 +400,10 @@ sub mouse_event { $self->Refresh; $self->Update; } else { + # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y, + # an converts the screen space coordinate to unscaled object space. + my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos); + # Select volume in this 3D canvas. # Don't deselect a volume if layer editing is enabled. We want the object to stay selected # during the scene manipulation. @@ -427,9 +431,6 @@ sub mouse_event { if ($volume_idx != -1) { if ($e->LeftDown && $self->enable_moving) { - # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y, - # an converts the screen space coordinate to unscaled object space. - my $pos3d = $self->mouse_to_3d(@$pos); # Only accept the initial position, if it is inside the volume bounding box. my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box; $volume_bbox->offset(1.); @@ -948,6 +949,9 @@ sub mulquats { sub mouse_to_3d { my ($self, $x, $y, $z) = @_; + return unless $self->GetContext; + $self->SetCurrent($self->GetContext); + my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 442d0abc95..f5a9021c02 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -217,7 +217,7 @@ sub _init_tabpanel { $self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ; # A variable to inform C++ Tab implementation about user_agent $self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ; - Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, $self->{no_controller}, + Slic3r::GUI::create_preset_tabs($self->{no_controller}, $self->{is_disabled_button_browse}, $self->{is_user_agent}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT, diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d48e31462e..a99bbddefe 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -389,9 +389,12 @@ sub new { }); }); $presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); - $presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 0); + $presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 1); } } + + my $frequently_changed_parameters_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + Slic3r::GUI::add_frequently_changed_parameters($self, $frequently_changed_parameters_sizer, $presets); my $object_info_sizer; { @@ -473,6 +476,7 @@ sub new { my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; + $right_sizer->Add($frequently_changed_parameters_sizer, 0, wxEXPAND | wxTOP, 10) if defined $frequently_changed_parameters_sizer; $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5); $right_sizer->Add($self->{list}, 1, wxEXPAND, 5); $right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0); diff --git a/resources/icons/action_undo.png b/resources/icons/action_undo.png new file mode 100644 index 0000000000..866ae9773b Binary files /dev/null and b/resources/icons/action_undo.png differ diff --git a/resources/icons/arrow_undo.png b/resources/icons/arrow_undo.png new file mode 100644 index 0000000000..6972c5e594 Binary files /dev/null and b/resources/icons/arrow_undo.png differ diff --git a/resources/localization/uk/Slic3rPE_.mo b/resources/localization/uk/Slic3rPE_.mo new file mode 100644 index 0000000000..c980ae64db Binary files /dev/null and b/resources/localization/uk/Slic3rPE_.mo differ diff --git a/slic3r.pl b/slic3r.pl index a3cc3cfc2f..d9bed0ab66 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -12,7 +12,7 @@ BEGIN { use File::Basename qw(basename); use Getopt::Long qw(:config no_auto_abbrev); use List::Util qw(first); -use POSIX qw(setlocale LC_NUMERIC); +#use POSIX qw(setlocale LC_NUMERIC); use Slic3r; use Slic3r::Geometry qw(deg2rad); use Time::HiRes qw(gettimeofday tv_interval); @@ -112,7 +112,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3 $Slic3r::GUI::autosave = $opt{autosave}; } $gui = Slic3r::GUI->new; - setlocale(LC_NUMERIC, 'C'); + #setlocale(LC_NUMERIC, 'C'); $gui->{mainframe}->load_config_file($_) for @{$opt{load}}; $gui->{mainframe}->load_config($cli_config); my @input_files = @ARGV; diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 045c55d96c..aed7ba12f6 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -18,6 +18,17 @@ namespace Slic3r { namespace GUI { wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None); } + void Field::PostInitialize(){ + m_Undo_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + // use bouth of temporary_icons till don't have "undo_icon" + auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + if (wxMSW) m_Undo_btn->SetBackgroundColour(color); + m_Undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); + m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); + + BUILD(); + } + void Field::on_kill_focus(wxEvent& event) { // Without this, there will be nasty focus bugs on Windows. // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all @@ -34,6 +45,12 @@ namespace Slic3r { namespace GUI { m_on_change(m_opt_id, get_value()); } + void Field::on_back_to_initial_value() + { + if (m_back_to_initial_value != nullptr && m_is_modified_value) + m_back_to_initial_value(m_opt_id); + } + wxString Field::get_tooltip_text(const wxString& default_string) { wxString tooltip_text(""); @@ -187,6 +204,17 @@ void CheckBox::BUILD() { window = dynamic_cast(temp); } +boost::any CheckBox::get_value() +{ + boost::any ret_val; + bool value = dynamic_cast(window)->GetValue(); + if (m_opt.type == coBool) + ret_val = static_cast(value); + else + ret_val = static_cast(value); + return ret_val; +} + int undef_spin_val = -9999; //! Probably, It's not necessary void SpinCtrl::BUILD() { @@ -217,8 +245,13 @@ void SpinCtrl::BUILD() { break; } + const int min_val = m_opt_id == "standby_temperature_delta" ? + -500 : m_opt.min > 0 ? + m_opt.min : 0; + const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; + auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, - 0, m_opt.min >0 ? m_opt.min : 0, m_opt.max < 2147483647 ? m_opt.max : 2147483647, default_value); + 0, min_val, max_val, default_value); temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { tmp_value = undef_spin_val; on_kill_focus(e); }), temp->GetId()); @@ -261,8 +294,10 @@ void Choice::BUILD() { if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()){ } else{ - for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) - temp->Append(wxString(el)); + for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){ + const wxString& str = m_opt_id == "support" ? L_str(el) : el; + temp->Append(str); + } set_selection(); } temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); @@ -411,6 +446,9 @@ boost::any Choice::get_value() boost::any ret_val; wxString ret_str = static_cast(window)->GetValue(); + if (m_opt_id == "support") + return ret_str; + if (m_opt.type != coEnum) ret_val = get_value_by_opt_type(ret_str); else diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index 492629b411..db8ad4c9f7 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -18,6 +18,13 @@ //#include "slic3r_gui.hpp" #include "GUI.hpp" +#include "Utils.hpp" + +#ifdef __WXMSW__ +#define wxMSW true +#else +#define wxMSW false +#endif namespace Slic3r { namespace GUI { @@ -25,13 +32,14 @@ class Field; using t_field = std::unique_ptr; using t_kill_focus = std::function; using t_change = std::function; +using t_back_to_init = std::function; wxString double_to_string(double const value); class Field { protected: // factory function to defer and enforce creation of derived type. - virtual void PostInitialize() { BUILD(); } + virtual void PostInitialize(); /// Finish constructing the Field's wxWidget-related properties, including setting its own sizer, etc. virtual void BUILD() = 0; @@ -42,6 +50,8 @@ protected: void on_kill_focus(wxEvent& event); /// Call the attached on_change method. void on_change_field(); + /// Call the attached m_back_to_initial_value method. + void on_back_to_initial_value(); public: /// parent wx item, opportunity to refactor (probably not necessary - data duplication) @@ -53,8 +63,13 @@ public: /// Function object to store callback passed in from owning object. t_change m_on_change {nullptr}; + /// Function object to store callback passed in from owning object. + t_back_to_init m_back_to_initial_value{ nullptr }; + // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; + // This is used to avoid recursive invocation of the field change/update by wxWidgets. + bool m_is_modified_value {false}; /// Copy of ConfigOption for deduction purposes const ConfigOptionDef m_opt {ConfigOptionDef()}; @@ -72,6 +87,9 @@ public: virtual void enable() = 0; virtual void disable() = 0; + wxStaticText* m_Label = nullptr; + wxButton* m_Undo_btn = nullptr; + /// Fires the enable or disable function, based on the input. inline void toggle(bool en) { en ? enable() : disable(); } @@ -85,7 +103,7 @@ public: virtual wxWindow* getWindow() { return nullptr; } bool is_matched(std::string string, std::string pattern); - boost::any get_value_by_opt_type(wxString str); + boost::any get_value_by_opt_type(wxString str); /// Factory method for generating new derived classes. template @@ -153,9 +171,7 @@ public: dynamic_cast(window)->SetValue(boost::any_cast(value)); m_disable_change_event = false; } - boost::any get_value() override { - return boost::any(dynamic_cast(window)->GetValue()); - } + boost::any get_value() override; void enable() override { dynamic_cast(window)->Enable(); } void disable() override { dynamic_cast(window)->Disable(); } diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 6b8613cb0f..262d41a799 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -45,6 +45,7 @@ #include "AppConfig.hpp" #include "Utils.hpp" #include "Preferences.hpp" +#include "PresetBundle.hpp" namespace Slic3r { namespace GUI { @@ -172,11 +173,15 @@ wxApp *g_wxApp = nullptr; wxFrame *g_wxMainFrame = nullptr; wxNotebook *g_wxTabPanel = nullptr; AppConfig *g_AppConfig = nullptr; +PresetBundle *g_PresetBundle= nullptr; std::vector g_tabs_list; wxLocale* g_wxLocale; +std::shared_ptr m_optgroup; +double m_brim_width = 0.0; + void set_wxapp(wxApp *app) { g_wxApp = app; @@ -197,6 +202,11 @@ void set_app_config(AppConfig *app_config) g_AppConfig = app_config; } +void set_preset_bundle(PresetBundle *preset_bundle) +{ + g_PresetBundle = preset_bundle; +} + std::vector& get_tabs_list() { return g_tabs_list; @@ -240,6 +250,7 @@ bool select_language(wxArrayString & names, g_wxLocale->Init(identifiers[index]); g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); g_wxLocale->AddCatalog(g_wxApp->GetAppName()); + wxSetlocale(LC_NUMERIC, "C"); return true; } return false; @@ -268,6 +279,7 @@ bool load_language() g_wxLocale->Init(identifiers[i]); g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); g_wxLocale->AddCatalog(g_wxApp->GetAppName()); + wxSetlocale(LC_NUMERIC, "C"); return true; } } @@ -346,15 +358,13 @@ void open_preferences_dialog(int event_preferences) dlg->ShowModal(); } -void create_preset_tabs(PresetBundle *preset_bundle, - bool no_controller, bool is_disabled_button_browse, bool is_user_agent, +void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, int event_value_change, int event_presets_changed, int event_button_browse, int event_button_test) { - add_created_tab(new TabPrint (g_wxTabPanel, no_controller), preset_bundle); - add_created_tab(new TabFilament (g_wxTabPanel, no_controller), preset_bundle); - add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent), - preset_bundle); + add_created_tab(new TabPrint (g_wxTabPanel, no_controller)); + add_created_tab(new TabFilament (g_wxTabPanel, no_controller)); + add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent)); for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) { Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); if (! tab) @@ -419,9 +429,13 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast(value))); break; case coStrings:{ - if (opt_key.compare("compatible_printers") == 0){ + if (opt_key.compare("compatible_printers") == 0 || + config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){ config.option(opt_key)->values.resize(0); - for (auto el : boost::any_cast>(value)) + std::vector values = boost::any_cast>(value); + if (values.size() == 1 && values[0] == "") + break; + for (auto el : values) config.option(opt_key)->values.push_back(el); } else{ @@ -434,7 +448,7 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast(value))); break; case coBools:{ - ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast(value) }; + ConfigOptionBools* vec_new = new ConfigOptionBools{ (bool)boost::any_cast(value) }; config.option(opt_key)->set_at(vec_new, opt_index, 0); break;} case coInt: @@ -458,9 +472,8 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b } break; case coPoints:{ - ConfigOptionPoints points; - points.values = boost::any_cast>(value); - config.set_key_value(opt_key, new ConfigOptionPoints(points)); + ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); } break; case coNone: @@ -475,9 +488,9 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b } } -void add_created_tab(Tab* panel, PresetBundle *preset_bundle) +void add_created_tab(Tab* panel) { - panel->create_preset_tab(preset_bundle); + panel->create_preset_tab(g_PresetBundle); // Load the currently selected preset into the GUI, update the preset selection box. panel->load_current_preset(); @@ -505,6 +518,11 @@ wxApp* get_app(){ return g_wxApp; } +wxColour* get_modified_label_clr() +{ + return new wxColour(253, 88, 0); +} + void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) { if (comboCtrl == nullptr) @@ -562,13 +580,13 @@ AppConfig* get_app_config() return g_AppConfig; } -wxString L_str(std::string str) +wxString L_str(const std::string &str) { //! Explicitly specify that the source string is already in UTF-8 encoding return wxGetTranslation(wxString(str.c_str(), wxConvUTF8)); } -wxString from_u8(std::string str) +wxString from_u8(const std::string &str) { return wxString::FromUTF8(str.c_str()); } @@ -587,4 +605,105 @@ wxWindow *get_widget_by_id(int id) return window; } +void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer) +{ + DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config; + m_optgroup = std::make_shared(parent, "", config); + const wxArrayInt& ar = preset_sizer->GetColWidths(); + m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front(); + m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){ + TabPrint* tab_print = nullptr; + for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { + Tab *tab = dynamic_cast(g_wxTabPanel->GetPage(i)); + if (!tab) + continue; + if (tab->name() == "print"){ + tab_print = static_cast(tab); + break; + } + } + if (tab_print == nullptr) + return; + + if (opt_key == "fill_density"){ + value = m_optgroup->get_config_value(*config, opt_key); + tab_print->set_value(opt_key, value); + tab_print->update(); + } + else{ + DynamicPrintConfig new_conf = *config; + if (opt_key == "brim"){ + double new_val; + double brim_width = config->opt_float("brim_width"); + if (boost::any_cast(value) == true) + { + new_val = m_brim_width == 0.0 ? 10 : + m_brim_width < 0.0 ? m_brim_width * (-1) : + m_brim_width; + } + else{ + m_brim_width = brim_width * (-1); + new_val = 0; + } + new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); + } + else{ //(opt_key == "support") + const wxString& selection = boost::any_cast(value); + + auto support_material = selection == _("None") ? false : true; + new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); + + if (selection == _("Everywhere")) + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); + else if (selection == _("Support on build plate only")) + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); + } + tab_print->load_config(new_conf); + } + + tab_print->update_dirty(); + }; + + const int width = 250; + Option option = m_optgroup->get_option("fill_density"); + option.opt.sidetext = ""; + option.opt.width = width; + m_optgroup->append_single_option_line(option); + + ConfigOptionDef def; + + def.label = L("Support"); + def.type = coStrings; + def.gui_type = "select_open"; + def.tooltip = L("Select what kind of support do you need"); + def.enum_labels.push_back(L("None")); + def.enum_labels.push_back(L("Support on build plate only")); + def.enum_labels.push_back(L("Everywhere")); + std::string selection = !config->opt_bool("support_material") ? + "None" : + config->opt_bool("support_material_buildplate_only") ? + "Support on build plate only" : + "Everywhere"; + def.default_value = new ConfigOptionStrings { selection }; + option = Option(def, "support"); + option.opt.width = width; + m_optgroup->append_single_option_line(option); + + m_brim_width = config->opt_float("brim_width"); + def.label = L("Brim"); + def.type = coBool; + def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); + def.gui_type = ""; + def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }; + option = Option(def, "brim"); + m_optgroup->append_single_option_line(option); + + sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxBottom, 1); +} + +ConfigOptionsGroup* get_optgroup() +{ + return m_optgroup.get(); +} + } } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index d9760ebf39..2baa10cb95 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -15,6 +15,9 @@ class wxComboCtrl; class wxString; class wxArrayString; class wxArrayLong; +class wxColour; +class wxBoxSizer; +class wxFlexGridSizer; namespace Slic3r { @@ -36,11 +39,12 @@ class TabIface; #define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str() // Minimal buffer length for translated string (char buf[MIN_BUF_LENGTH_FOR_L]) -#define MIN_BUF_LENGTH_FOR_L 128 +#define MIN_BUF_LENGTH_FOR_L 512 namespace GUI { class Tab; +class ConfigOptionsGroup; // Map from an file_type name to full file wildcard name. typedef std::map t_file_wild_card; inline t_file_wild_card& get_file_wild_card() { @@ -70,9 +74,11 @@ void set_wxapp(wxApp *app); void set_main_frame(wxFrame *main_frame); void set_tab_panel(wxNotebook *tab_panel); void set_app_config(AppConfig *app_config); +void set_preset_bundle(PresetBundle *preset_bundle); AppConfig* get_app_config(); wxApp* get_app(); +wxColour* get_modified_label_clr(); void add_debug_menu(wxMenuBar *menu, int event_language_change); @@ -80,14 +86,13 @@ void add_debug_menu(wxMenuBar *menu, int event_language_change); void open_preferences_dialog(int event_preferences); // Create a new preset tab (print, filament and printer), -void create_preset_tabs(PresetBundle *preset_bundle, - bool no_controller, bool is_disabled_button_browse, bool is_user_agent, +void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, int event_value_change, int event_presets_changed, int event_button_browse, int event_button_test); TabIface* get_preset_tab_iface(char *name); // add it at the end of the tab panel. -void add_created_tab(Tab* panel, PresetBundle *preset_bundle); +void add_created_tab(Tab* panel); // Change option value in config void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0); @@ -118,12 +123,16 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string int combochecklist_get_flags(wxComboCtrl* comboCtrl); // Return translated std::string as a wxString -wxString L_str(std::string str); +wxString L_str(const std::string &str); // Return wxString from std::string in UTF8 -wxString from_u8(std::string str); +wxString from_u8(const std::string &str); wxWindow *get_widget_by_id(int id); +void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); + +ConfigOptionsGroup* get_optgroup(); + } } diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index f88b3f8c08..0be24824c4 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -3,18 +3,21 @@ #include #include +#include +#include +#include "Utils.hpp" namespace Slic3r { namespace GUI { -const t_field& OptionsGroup::build_field(const Option& opt) { - return build_field(opt.opt_id, opt.opt); +const t_field& OptionsGroup::build_field(const Option& opt, wxStaticText* label/* = nullptr*/) { + return build_field(opt.opt_id, opt.opt, label); } -const t_field& OptionsGroup::build_field(const t_config_option_key& id) { +const t_field& OptionsGroup::build_field(const t_config_option_key& id, wxStaticText* label/* = nullptr*/) { const ConfigOptionDef& opt = m_options.at(id).opt; - return build_field(id, opt); + return build_field(id, opt, label); } -const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt) { +const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label/* = nullptr*/) { // Check the gui_type field first, fall through // is the normal type. if (opt.gui_type.compare("select") == 0) { @@ -72,7 +75,16 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co this->on_kill_focus(); }; field->m_parent = parent(); - // assign function objects for callbacks, etc. + + //! Label to change background color, when option is modified + field->m_Label = label; + field->m_back_to_initial_value = [this](std::string opt_id){ + if (!this->m_disabled) + this->back_to_initial_value(opt_id); + }; + if (!m_is_tab_opt) field->m_Undo_btn->Hide(); + + // assign function objects for callbacks, etc. return field; } @@ -100,6 +112,7 @@ void OptionsGroup::append_line(const Line& line) { const auto& option = option_set.front(); const auto& field = build_field(option); + sizer->Add(field->m_Undo_btn); if (is_window_field(field)) sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); if (is_sizer_field(field)) @@ -110,8 +123,9 @@ void OptionsGroup::append_line(const Line& line) { auto grid_sizer = m_grid_sizer; // Build a label if we have it + wxStaticText* label=nullptr; if (label_width != 0) { - auto label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"), + label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"), wxDefaultPosition, wxSize(label_width, -1)); label->SetFont(label_font); label->Wrap(label_width); // avoid a Linux/GTK bug @@ -128,25 +142,24 @@ void OptionsGroup::append_line(const Line& line) { } // if we have a single option with no sidetext just add it directly to the grid sizer - if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && - option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { - const auto& option = option_set.front(); - const auto& field = build_field(option); -//! std::cerr << "single option, no sidetext.\n"; -//! std::cerr << "field parent is not null?: " << (field->parent != nullptr) << "\n"; + auto sizer = new wxBoxSizer(wxHORIZONTAL); + grid_sizer->Add(sizer, 0, wxEXPAND | wxALL, 0); + if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && + option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { + const auto& option = option_set.front(); + const auto& field = build_field(option, label); - if (is_window_field(field)) - grid_sizer->Add(field->getWindow(), 0, (option.opt.full_width ? wxEXPAND : 0) | + sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL); + if (is_window_field(field)) + sizer->Add(field->getWindow(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2); - if (is_sizer_field(field)) - grid_sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); - return; - } + if (is_sizer_field(field)) + sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); + return; + } // if we're here, we have more than one option or a single option with sidetext // so we need a horizontal sizer to arrange these things - auto sizer = new wxBoxSizer(wxHORIZONTAL); - grid_sizer->Add(sizer, 0, wxEXPAND | wxALL, 0); for (auto opt : option_set) { ConfigOptionDef option = opt.opt; // add label if any @@ -156,14 +169,15 @@ void OptionsGroup::append_line(const Line& line) { // wxString str_label = (option.label == "Top" || option.label == "Bottom") ? // wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()): // L_str(option.label); - auto field_label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize); - field_label->SetFont(label_font); - sizer->Add(field_label, 0, wxALIGN_CENTER_VERTICAL, 0); + label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize); + label->SetFont(label_font); + sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0); } // add field const Option& opt_ref = opt; - auto& field = build_field(opt_ref); + auto& field = build_field(opt_ref, label); + sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL, 0); is_sizer_field(field) ? sizer->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) : sizer->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); @@ -244,6 +258,12 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val // # Currently used for the post_process config value only. // my @values = split / ; / , $field_value; // $self->config->set($opt_key, \@values); + std::string str = boost::any_cast(value); + if (str.back() == ';') + str.pop_back(); + std::vector values; + boost::split(values, str, boost::is_any_of(";")); + change_opt_value(*m_config, opt_key, values); } else { if (opt_index == -1) { @@ -263,6 +283,30 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this } +void ConfigOptionsGroup::back_to_initial_value(const std::string opt_key) +{ + if (m_get_initial_config == nullptr) + return; + DynamicPrintConfig config = m_get_initial_config(); + boost::any value; + if (opt_key == "extruders_count"){ + auto *nozzle_diameter = dynamic_cast(config.option("nozzle_diameter")); + value = int(nozzle_diameter->values.size()); + } + else if (m_opt_map.find(opt_key) != m_opt_map.end()) + { + auto opt_id = m_opt_map.find(opt_key)->first; + std::string opt_short_key = m_opt_map.at(opt_id).first; + int opt_index = m_opt_map.at(opt_id).second; + value = get_config_value(config, opt_short_key, opt_index); + } + else + value = get_config_value(config, opt_key); + + set_value(opt_key, value); + on_change_OG(opt_key, get_value(opt_key)); +} + void ConfigOptionsGroup::reload_config(){ for (std::map< std::string, std::pair >::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { auto opt_id = it->first; @@ -323,7 +367,7 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std: double val = opt->type == coFloats ? config.opt_float(opt_key, idx) : opt->type == coFloat ? config.opt_float(opt_key) : - config.option(opt_key)->values.at(idx); + config.option(opt_key)->get_at(idx); ret = double_to_string(val); } break; @@ -333,6 +377,12 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std: case coStrings: if (config.option(opt_key)->values.empty()) ret = text_value; + else if (opt->gui_flags.compare("serialized") == 0){ + std::vector values = config.option(opt_key)->values; + for (auto el : values) + text_value += el + ";"; + ret = text_value; + } else ret = static_cast(config.opt_string(opt_key, static_cast(idx))); break; @@ -363,10 +413,8 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std: ret = static_cast(config.option>(opt_key)->value); } break; - case coPoints:{ - const auto &value = *config.option(opt_key); - ret = value.values.at(idx); - } + case coPoints: + ret = config.option(opt_key)->get_at(idx); break; case coNone: default: @@ -376,6 +424,9 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std: } Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index){ + Field* field = get_field(opt_key); + if (field != nullptr) + return field; std::string opt_id = ""; for (std::map< std::string, std::pair >::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second){ diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp index 6e88d1d887..42db222254 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/xs/src/slic3r/GUI/OptionsGroup.hpp @@ -27,6 +27,9 @@ namespace Slic3r { namespace GUI { using widget_t = std::function;//!std::function; using column_t = std::function; +//auto default_label_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour +//auto modified_label_clr = *new wxColour(254, 189, 101); + /// Wraps a ConfigOptionDef and adds function object for creating a side_widget. struct Option { ConfigOptionDef opt { ConfigOptionDef() }; @@ -75,6 +78,7 @@ public: wxSizer* sizer {nullptr}; column_t extra_column {nullptr}; t_change m_on_change {nullptr}; + std::function m_get_initial_config{ nullptr }; wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; @@ -109,8 +113,8 @@ public: inline void enable() { for (auto& field : m_fields) field.second->enable(); } inline void disable() { for (auto& field : m_fields) field.second->disable(); } - OptionsGroup(wxWindow* _parent, wxString title) : - m_parent(_parent), title(title) { + OptionsGroup(wxWindow* _parent, wxString title, bool is_tab_opt=false) : + m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") { sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); auto num_columns = 1U; if (label_width != 0) num_columns++; @@ -132,22 +136,25 @@ protected: t_optionfield_map m_fields; bool m_disabled {false}; wxGridSizer* m_grid_sizer {nullptr}; + // "true" if option is created in preset tabs + bool m_is_tab_opt{ false }; /// Generate a wxSizer or wxWindow from a configuration option /// Precondition: opt resolves to a known ConfigOption /// Postcondition: fields contains a wx gui object. - const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt); - const t_field& build_field(const t_config_option_key& id); - const t_field& build_field(const Option& opt); + const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label = nullptr); + const t_field& build_field(const t_config_option_key& id, wxStaticText* label = nullptr); + const t_field& build_field(const Option& opt, wxStaticText* label = nullptr); virtual void on_kill_focus (){}; virtual void on_change_OG(t_config_option_key opt_id, boost::any value); + virtual void back_to_initial_value(const std::string opt_key){}; }; class ConfigOptionsGroup: public OptionsGroup { public: - ConfigOptionsGroup(wxWindow* parent, wxString title, DynamicPrintConfig* _config = nullptr) : - OptionsGroup(parent, title), m_config(_config) {} + ConfigOptionsGroup(wxWindow* parent, wxString title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) : + OptionsGroup(parent, title, is_tab_opt), m_config(_config) {} /// reference to libslic3r config, non-owning pointer (?). DynamicPrintConfig* m_config {nullptr}; @@ -169,10 +176,8 @@ public: } void on_change_OG(t_config_option_key opt_id, boost::any value) override; - void on_kill_focus() override - { - reload_config(); - } + void back_to_initial_value(const std::string opt_key) override; + void on_kill_focus() override{ reload_config();} void reload_config(); boost::any config_value(std::string opt_key, int opt_index, bool deserialize); // return option value from config diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/xs/src/slic3r/GUI/PresetHints.cpp index 07c8e3b072..d4c929c1c6 100644 --- a/xs/src/slic3r/GUI/PresetHints.cpp +++ b/xs/src/slic3r/GUI/PresetHints.cpp @@ -13,10 +13,11 @@ namespace Slic3r { +#define MIN_BUF_LENGTH 4096 std::string PresetHints::cooling_description(const Preset &preset) { std::string out; - char buf[4096]; + char buf[MIN_BUF_LENGTH/*4096*/]; if (preset.config.opt_bool("cooling", 0)) { int slowdown_below_layer_time = preset.config.opt_int("slowdown_below_layer_time", 0); int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); @@ -220,7 +221,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle + _CHB(L(" with a volumetric rate ")); if (limited_by_max_volumetric_speed) max_flow = max_volumetric_speed; - char buf[2048]; + char buf[MIN_BUF_LENGTH/*2048*/]; sprintf(buf, _CHB(L("%3.2f mm³/s")), max_flow); out += buf; sprintf(buf, _CHB(L(" at filament speed %3.2f mm/s.")), max_flow / filament_crossection); @@ -258,7 +259,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre if (num_perimeters > 0) { int num_lines = std::min(num_perimeters * 2, 10); - char buf[256]; + char buf[MIN_BUF_LENGTH/*256*/]; sprintf(buf, _CHB(L("Recommended object thin wall thickness for layer height %.2f and ")), layer_height); out += buf; // Start with the width of two closely spaced diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index 7ce6790a12..858eb8bcfd 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -33,19 +33,23 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) // preset chooser m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY); - const wxBitmap* bmp = new wxBitmap(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG); + + auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //buttons wxBitmap bmpMenu; bmpMenu = wxBitmap(from_u8(Slic3r::var("disk.png")), wxBITMAP_TYPE_PNG); m_btn_save_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + if (wxMSW) m_btn_save_preset->SetBackgroundColour(color); bmpMenu = wxBitmap(from_u8(Slic3r::var("delete.png")), wxBITMAP_TYPE_PNG); m_btn_delete_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color); m_show_incompatible_presets = false; m_bmp_show_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG); m_bmp_hide_incompatible_presets = new wxBitmap(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG); m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, *m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + if (wxMSW) m_btn_hide_incompatible_presets->SetBackgroundColour(color); m_btn_save_preset->SetToolTip(_(L("Save current ")) + m_title); m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); @@ -93,6 +97,7 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) if (selected_item >= 0){ std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); select_preset(selected_string); + update_changed_ui(); } })); @@ -134,11 +139,93 @@ PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder return page; } +template +void add_correct_opts_to_dirty_options(const std::string &opt_key, std::vector *vec, TabPrinter *tab) +{ + auto opt_init = static_cast(tab->m_presets->get_selected_preset().config.option(opt_key)); + auto opt_cur = static_cast(tab->m_config->option(opt_key)); + int opt_init_max_id = opt_init->values.size()-1; + for (int i = 0; i < opt_cur->values.size(); i++) + { + int init_id = i <= opt_init_max_id ? i : 0; + if (opt_cur->values[i] != opt_init->values[init_id]) + vec->emplace_back(opt_key + "#" + std::to_string(i)); + } +} + +// Update UI according to changes +void Tab::update_changed_ui() +{ + auto dirty_options = m_presets->current_dirty_options(); + + if (name() == "printer"){ + // Update dirty_options in case changes of Extruder's options + TabPrinter* tab = static_cast(this); + std::vector new_dirty; + for (auto opt_key : dirty_options) + { + switch (m_config->option(opt_key)->type()) + { + case coInts: add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + case coBools: add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + case coFloats: add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + case coStrings: add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + case coPercents:add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + case coPoints: add_correct_opts_to_dirty_options(opt_key, &new_dirty, tab); break; + default: new_dirty.emplace_back(opt_key); break; + } + } + + dirty_options.resize(0); + dirty_options = new_dirty; + if (tab->m_initial_extruders_count != tab->m_extruders_count){ + dirty_options.emplace_back("extruders_count"); + } + } + + // Add new dirty options to m_dirty_options + for (auto opt_key : dirty_options){ + Field* field = get_field(opt_key); + if (field != nullptr && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()){ + if (field->m_Label != nullptr){ + field->m_Label->SetForegroundColour(*get_modified_label_clr()); + field->m_Label->Refresh(true); + } + field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(wxMSW ? var("action_undo.png") : var("arrow_undo.png")), wxBITMAP_TYPE_PNG)); + field->m_is_modified_value = true; + + m_dirty_options.push_back(opt_key); + } + } + + // Delete clear options from m_dirty_options + for (auto i = 0; i < m_dirty_options.size(); ++i) + { + const std::string &opt_key = m_dirty_options[i]; + Field* field = get_field(opt_key); + if (field != nullptr && find(dirty_options.begin(), dirty_options.end(), opt_key) == dirty_options.end()) + { + field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); + if (field->m_Label != nullptr){ + field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT); + field->m_Label->Refresh(true); + } + field->m_is_modified_value = false; + std::vector::iterator itr = find(m_dirty_options.begin(), m_dirty_options.end(), opt_key); + if (itr != m_dirty_options.end()){ + m_dirty_options.erase(itr); + --i; + } + } + } +} + // Update the combo box label of the selected preset based on its "dirty" state, // comparing the selected preset config with $self->{config}. void Tab::update_dirty(){ m_presets->update_dirty_ui(m_presets_choice); - on_presets_changed(); + on_presets_changed(); + update_changed_ui(); } void Tab::update_tab_ui() @@ -146,81 +233,13 @@ void Tab::update_tab_ui() m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); } -template -boost::any get_new_value(const DynamicPrintConfig &config_new, const DynamicPrintConfig &config_old, std::string opt_key, int &index) -{ - for (int i = 0; i < config_new.option(opt_key)->values.size(); i++) - if (config_new.option(opt_key)->values[i] != - config_old.option(opt_key)->values[i]){ - index = i; - break; - } - return config_new.option(opt_key)->values[index]; -} - // Load a provied DynamicConfig into the tab, modifying the active preset. // This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. void Tab::load_config(DynamicPrintConfig config) { bool modified = 0; - boost::any value; - int opt_index = 0; for(auto opt_key : m_config->diff(config)) { - switch ( config.def()->get(opt_key)->type ){ - case coFloatOrPercent: - value = config.option(opt_key)->value; - break; - case coPercent: - value = config.option(opt_key)->value; - break; - case coFloat: - value = config.opt_float(opt_key); - break; - case coString: - value = config.opt_string(opt_key); - break; - case coPercents: - value = get_new_value(config, *m_config, opt_key, opt_index); - break; - case coFloats: - value = get_new_value(config, *m_config, opt_key, opt_index); - break; - case coStrings: - value = config.option(opt_key)->values.empty() ? "" : - get_new_value(config, *m_config, opt_key, opt_index); - break; - case coBool: - value = config.opt_bool(opt_key); - break; - case coBools: - value = get_new_value(config, *m_config, opt_key, opt_index); - break; - case coInt: - value = config.opt_int(opt_key); - break; - case coInts: - value = get_new_value(config, *m_config, opt_key, opt_index); - break; - case coEnum:{ - if (opt_key.compare("external_fill_pattern") == 0 || - opt_key.compare("fill_pattern") == 0) - value = config.option>(opt_key)->value; - else if (opt_key.compare("gcode_flavor") == 0) - value = config.option>(opt_key)->value; - else if (opt_key.compare("support_material_pattern") == 0) - value = config.option>(opt_key)->value; - else if (opt_key.compare("seam_position") == 0) - value = config.option>(opt_key)->value; - } - break; - case coPoints: - break; - case coNone: - break; - default: - break; - } - change_opt_value(*m_config, opt_key, value, opt_index); + m_config->set_key_value(opt_key, config.option(opt_key)->clone()); modified = 1; } if (modified) { @@ -291,6 +310,27 @@ void Tab::on_value_change(std::string opt_key, boost::any value) } g_wxMainFrame->ProcessWindowEvent(event); } + if (opt_key == "fill_density") + { + value = get_optgroup()->get_config_value(*m_config, opt_key); + get_optgroup()->set_value(opt_key, value); + } + if (opt_key == "support_material" || opt_key == "support_material_buildplate_only") + { + wxString new_selection = !m_config->opt_bool("support_material") ? + _("None") : + m_config->opt_bool("support_material_buildplate_only") ? + _("Support on build plate only") : + _("Everywhere"); + get_optgroup()->set_value("support", new_selection); + } + if (opt_key == "brim_width") + { + bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; + get_optgroup()->set_value("brim", val); + } + + update(); } @@ -308,6 +348,22 @@ void Tab::on_presets_changed() } } +void Tab::update_frequently_changed_parameters() +{ + boost::any value = get_optgroup()->get_config_value(*m_config, "fill_density"); + get_optgroup()->set_value("fill_density", value); + + wxString new_selection = !m_config->opt_bool("support_material") ? + _("None") : + m_config->opt_bool("support_material_buildplate_only") ? + _("Support on build plate only") : + _("Everywhere"); + get_optgroup()->set_value("support", new_selection); + + bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; + get_optgroup()->set_value("brim", val); +} + void Tab::reload_compatible_printers_widget() { bool has_any = !m_config->option("compatible_printers")->values.empty(); @@ -576,7 +632,8 @@ void TabPrint::update() DynamicPrintConfig new_conf = *m_config; if (dialog->ShowModal() == wxID_YES) { const auto &val = *m_config->option("first_layer_height"); - new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, val.percent)); + auto percent = val.percent; + new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, percent)); if (m_config->opt_float("layer_height") < 0.15) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.15)); if (m_config->opt_float("layer_height") > 0.35) new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.35)); @@ -674,13 +731,16 @@ void TabPrint::update() "\nShall I switch to rectilinear fill pattern?")); auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); DynamicPrintConfig new_conf = *m_config; + double fill_density; if (dialog->ShowModal() == wxID_YES) { new_conf.set_key_value("fill_pattern", new ConfigOptionEnum(ipRectilinear)); - new_conf.set_key_value("fill_density", new ConfigOptionPercent(100)); + fill_density = 100; } else - new_conf.set_key_value("fill_density", new ConfigOptionPercent(40)); + fill_density = 40; + new_conf.set_key_value("fill_density", new ConfigOptionPercent(fill_density)); load_config(new_conf); + on_value_change("fill_density", fill_density); } } } @@ -943,14 +1003,14 @@ void TabPrinter::build() auto default_config = m_preset_bundle->full_config(); auto *nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); - m_extruders_count = nozzle_diameter->values.size(); + m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); auto page = add_options_page(_(L("General")), "printer_empty.png"); auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); Line line{ _(L("Bed shape")), "" }; line.widget = [this](wxWindow* parent){ - auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); // btn->SetFont(Slic3r::GUI::small_font); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); @@ -1188,6 +1248,7 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){ m_preset_bundle->printers.get_edited_preset().set_num_extruders(extruders_count); m_preset_bundle->update_multi_material_filament_presets(); build_extruder_pages(); + reload_config(); on_value_change("extruders_count", extruders_count); } @@ -1326,8 +1387,9 @@ void TabPrinter::update(){ DynamicPrintConfig new_conf = *m_config; if (dialog->ShowModal() == wxID_YES) { - auto wipe = static_cast(m_config->option("wipe")); - wipe->values[i] = 0; + auto wipe = static_cast(m_config->option("wipe")->clone()); + for (int w = 0; w < wipe->values.size(); w++) + wipe->values[w] = false; new_conf.set_key_value("wipe", wipe); } else { @@ -1350,15 +1412,13 @@ void TabPrinter::update(){ void Tab::load_current_preset() { auto preset = m_presets->get_edited_preset(); -// try{ -// local $SIG{ __WARN__ } = Slic3r::GUI::warning_catcher($self); - preset.is_default ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); - update(); - // For the printer profile, generate the extruder pages. - on_preset_loaded(); - // Reload preset pages with the new configuration values. - reload_config(); -// }; + preset.is_default ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); + update(); + // For the printer profile, generate the extruder pages. + on_preset_loaded(); + // Reload preset pages with the new configuration values. + reload_config(); + // use CallAfter because some field triggers schedule on_change calls using CallAfter, // and we don't want them to be called after this update_dirty() as they would mark the // preset dirty again @@ -1369,6 +1429,11 @@ void Tab::load_current_preset() return; update_tab_ui(); on_presets_changed(); + + if (name() == "print"){ + update_frequently_changed_parameters(); + update_changed_ui(); + } }); } @@ -1737,7 +1802,7 @@ bool Page::set_value(t_config_option_key opt_key, boost::any value){ ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_width /*= -1*/) { //! config_ have to be "right" - ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config); + ConfigOptionsGroupShp optgroup = std::make_shared(this, title, m_config, true); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; @@ -1751,6 +1816,11 @@ ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_wid //! }); }; + optgroup->m_get_initial_config = [this](){ + DynamicPrintConfig config = static_cast(GetParent())->m_presets->get_selected_preset().config; + return config; + }; + vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10); m_optgroups.push_back(optgroup); diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index 208f99fec2..e2dc51ee4e 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -99,6 +99,7 @@ protected: bool m_no_controller; std::vector m_reload_dependent_tabs = {}; + std::vector m_dirty_options = {}; // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. wxEventType m_event_value_change = 0; @@ -145,6 +146,7 @@ public: void toggle_show_hide_incompatible(); void update_show_hide_incompatible_button(); void update_ui_from_settings(); + void update_changed_ui(); PageShp add_options_page(wxString title, std::string icon, bool is_extruder_pages = false); @@ -171,6 +173,7 @@ public: protected: void on_presets_changed(); + void update_frequently_changed_parameters(); }; //Slic3r::GUI::Tab::Print; @@ -221,6 +224,7 @@ public: wxButton* m_octoprint_host_test_btn; size_t m_extruders_count; + size_t m_initial_extruders_count; std::vector m_extruder_pages; TabPrinter() {} diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index cae480c2f0..be36c531fd 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -35,12 +35,10 @@ void set_tab_panel(SV *ui) void add_debug_menu(SV *ui, int event_language_change) %code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %}; -void create_preset_tabs(PresetBundle *preset_bundle, bool no_controller, - bool is_disabled_button_browse, bool is_user_agent, +void create_preset_tabs(bool no_controller, bool is_disabled_button_browse, bool is_user_agent, int event_value_change, int event_presets_changed, int event_button_browse, int event_button_test) - %code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, no_controller, - is_disabled_button_browse, is_user_agent, + %code%{ Slic3r::GUI::create_preset_tabs(no_controller, is_disabled_button_browse, is_user_agent, event_value_change, event_presets_changed, event_button_browse, event_button_test); %}; @@ -61,3 +59,11 @@ void set_app_config(AppConfig *app_config) void open_preferences_dialog(int preferences_event) %code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %}; + +void set_preset_bundle(PresetBundle *preset_bundle) + %code%{ Slic3r::GUI::set_preset_bundle(preset_bundle); %}; + +void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_sizer) + %code%{ Slic3r::GUI::add_frequently_changed_parameters((wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"), + (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"), + (wxFlexGridSizer*)wxPli_sv_2_object(aTHX_ ui_p_sizer, "Wx::FlexGridSizer")); %};