diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 886c8f419..fbcd34a3f 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -149,6 +149,7 @@ sub _init_tabpanel { if (defined $presets){ my $reload_dependent_tabs = $tab->get_dependent_tabs; $self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets); + $self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item; if ($tab_name eq 'printer') { # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. for my $tab_name_other (qw(print filament)) { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ce78eab8e..647ae4ea6 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -514,6 +514,13 @@ sub new { $self->SetSizer($sizer); } + # Last correct selected item for each preset + { + $self->{selected_item_print} = 0; + $self->{selected_item_filament} = 0; + $self->{selected_item_printer} = 0; + } + $self->update_ui_from_settings(); return $self; @@ -538,9 +545,21 @@ sub _on_select_preset { # Only update the platter UI for the 2nd and other filaments. wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice); } else { + my $selected_item = $choice->GetSelection(); + return if ($selected_item == $self->{"selected_item_$group"}); + + my $selected_string = $choice->GetString($selected_item); + if ($selected_string eq "------- System presets -------" || + $selected_string eq "------- User presets -------"){ + $choice->SetSelection($self->{"selected_item_$group"}); + return; + } + # call GetSelection() in scalar context as it's context-aware - $self->{on_select_preset}->($group, $choice->GetStringSelection) - if $self->{on_select_preset}; +# $self->{on_select_preset}->($group, $choice->GetStringSelection) + $self->{on_select_preset}->($group, $selected_string) + if $self->{on_select_preset}; + $self->{"selected_item_$group"} = $selected_item; } # Synchronize config.ini with the current selections. wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config}); diff --git a/resources/icons/action_undo.png b/resources/icons/action_undo.png index 877f15986..06bb98b22 100644 Binary files a/resources/icons/action_undo.png and b/resources/icons/action_undo.png differ diff --git a/resources/icons/action_undo_grey.png b/resources/icons/action_undo_grey.png new file mode 100644 index 000000000..eaa1593e3 Binary files /dev/null and b/resources/icons/action_undo_grey.png differ diff --git a/resources/icons/question_mark_01.png b/resources/icons/question_mark_01.png new file mode 100644 index 000000000..25814a61d Binary files /dev/null and b/resources/icons/question_mark_01.png differ diff --git a/resources/icons/sys_unlock_grey.png b/resources/icons/sys_unlock_grey.png new file mode 100644 index 000000000..0dedf4dee Binary files /dev/null and b/resources/icons/sys_unlock_grey.png differ diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 2f03daaa7..5f1c11951 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -212,6 +212,8 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/RammingChart.hpp ${LIBDIR}/slic3r/GUI/BonjourDialog.cpp ${LIBDIR}/slic3r/GUI/BonjourDialog.hpp + ${LIBDIR}/slic3r/GUI/ButtonsDescription.cpp + ${LIBDIR}/slic3r/GUI/ButtonsDescription.hpp ${LIBDIR}/slic3r/Config/Snapshot.cpp ${LIBDIR}/slic3r/Config/Snapshot.hpp ${LIBDIR}/slic3r/Config/Version.cpp diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index a4eaf3072..8c1349e08 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -206,6 +206,44 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const return diff; } +template +void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase *this_c) +{ + const T* opt_init = static_cast(other.option(opt_key)); + const T* opt_cur = static_cast(this_c->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)); + } +} + +t_config_option_keys ConfigBase::deep_diff(const ConfigBase &other) const +{ + t_config_option_keys diff; + for (const t_config_option_key &opt_key : this->keys()) { + const ConfigOption *this_opt = this->option(opt_key); + const ConfigOption *other_opt = other.option(opt_key); + if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt) + { + if (opt_key == "bed_shape"){ diff.emplace_back(opt_key); continue; } + switch (other_opt->type()) + { + case coInts: add_correct_opts_to_diff(opt_key, diff, other, this); break; + case coBools: add_correct_opts_to_diff(opt_key, diff, other, this); break; + case coFloats: add_correct_opts_to_diff(opt_key, diff, other, this); break; + case coStrings: add_correct_opts_to_diff(opt_key, diff, other, this); break; + case coPercents:add_correct_opts_to_diff(opt_key, diff, other, this); break; + case coPoints: add_correct_opts_to_diff(opt_key, diff, other, this); break; + default: diff.emplace_back(opt_key); break; + } + } + } + return diff; +} + t_config_option_keys ConfigBase::equal(const ConfigBase &other) const { t_config_option_keys equal; diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 06db9efef..14050c6cc 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -1047,6 +1047,9 @@ public: void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false); bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } t_config_option_keys diff(const ConfigBase &other) const; + // Use deep_diff to correct return of changed options, + // considering individual options for each extruder + t_config_option_keys deep_diff(const ConfigBase &other) const; t_config_option_keys equal(const ConfigBase &other) const; std::string serialize(const t_config_option_key &opt_key) const; // Set a configuration value from a string, it will call an overridable handle_legacy() diff --git a/xs/src/slic3r/GUI/2DBed.hpp b/xs/src/slic3r/GUI/2DBed.hpp index a170b708a..4b14986a2 100644 --- a/xs/src/slic3r/GUI/2DBed.hpp +++ b/xs/src/slic3r/GUI/2DBed.hpp @@ -1,3 +1,6 @@ +#ifndef slic3r_2DBed_hpp_ +#define slic3r_2DBed_hpp_ + #include #include "Config.hpp" @@ -45,3 +48,5 @@ public: } // GUI } // Slic3r + +#endif /* slic3r_2DBed_hpp_ */ diff --git a/xs/src/slic3r/GUI/BedShapeDialog.hpp b/xs/src/slic3r/GUI/BedShapeDialog.hpp index f4614c342..5ff488063 100644 --- a/xs/src/slic3r/GUI/BedShapeDialog.hpp +++ b/xs/src/slic3r/GUI/BedShapeDialog.hpp @@ -1,3 +1,5 @@ +#ifndef slic3r_BedShapeDialog_hpp_ +#define slic3r_BedShapeDialog_hpp_ // The bed shape dialog. // The dialog opens from Print Settins tab->Bed Shape : Set... @@ -49,3 +51,6 @@ public: } // GUI } // Slic3r + + +#endif /* slic3r_BedShapeDialog_hpp_ */ diff --git a/xs/src/slic3r/GUI/ButtonsDescription.cpp b/xs/src/slic3r/GUI/ButtonsDescription.cpp new file mode 100644 index 000000000..b932985bf --- /dev/null +++ b/xs/src/slic3r/GUI/ButtonsDescription.cpp @@ -0,0 +1,44 @@ +#include "ButtonsDescription.hpp" +#include +#include +#include + +#include "GUI.hpp" + +namespace Slic3r { +namespace GUI { + +ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions) : + wxDialog(parent, wxID_ANY, "Buttons Description", wxDefaultPosition, wxDefaultSize), + m_icon_descriptions(icon_descriptions) +{ + auto grid_sizer = new wxFlexGridSizer(3, 20, 20); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(grid_sizer, 0, wxEXPAND | wxALL, 20); + + for (auto pair : *m_icon_descriptions) + { + auto icon = new wxStaticBitmap(this, wxID_ANY, *pair.first); + grid_sizer->Add(icon, -1, wxALIGN_CENTRE_VERTICAL); + + std::istringstream f(pair.second); + std::string s; + getline(f, s, ';'); + auto description = new wxStaticText(this, wxID_ANY, _(s)); + grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL); + getline(f, s, ';'); + description = new wxStaticText(this, wxID_ANY, _(s)); + grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); + } + + auto button = CreateStdDialogButtonSizer(wxOK); + main_sizer->Add(button, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); +} + +} // GUI +} // Slic3r + diff --git a/xs/src/slic3r/GUI/ButtonsDescription.hpp b/xs/src/slic3r/GUI/ButtonsDescription.hpp new file mode 100644 index 000000000..4858eaaea --- /dev/null +++ b/xs/src/slic3r/GUI/ButtonsDescription.hpp @@ -0,0 +1,27 @@ +#ifndef slic3r_ButtonsDescription_hpp +#define slic3r_ButtonsDescription_hpp + +#include +#include + +namespace Slic3r { +namespace GUI { + +using t_icon_descriptions = std::vector>; + +class ButtonsDescription : public wxDialog +{ + t_icon_descriptions* m_icon_descriptions; +public: + ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions); + ~ButtonsDescription(){} + + +}; + +} // GUI +} // Slic3r + + +#endif + diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index c68b2304c..d0a2ccec2 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -20,23 +20,22 @@ namespace Slic3r { namespace GUI { void Field::PostInitialize(){ auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - m_Undo_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - m_Undo_to_sys_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + auto sz = 16; + #ifdef __WXGTK__ + sz = 28; + #endif // __WXGTK__ + m_Undo_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxSize(sz,sz), wxNO_BORDER); + m_Undo_to_sys_btn = new wxButton(m_parent, wxID_ANY, "", wxDefaultPosition, wxSize(sz,sz), wxNO_BORDER); if (wxMSW) { m_Undo_btn->SetBackgroundColour(color); m_Undo_to_sys_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(); })); m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); })); BUILD(); } - void Field::set_nonsys_btn_icon(const std::string& icon){ - m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG)); - } - 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 @@ -81,12 +80,13 @@ namespace Slic3r { namespace GUI { return std::regex_match(string, regex_pattern); } - boost::any Field::get_value_by_opt_type(wxString& str) +// boost::any Field::get_value_by_opt_type(wxString& str) + void Field::get_value_by_opt_type(wxString& str) { - boost::any ret_val; +// boost::any m_value; switch (m_opt.type){ case coInt: - ret_val = wxAtoi(str); + m_value = wxAtoi(str); break; case coPercent: case coPercents: @@ -96,18 +96,18 @@ namespace Slic3r { namespace GUI { str.RemoveLast(); double val; str.ToCDouble(&val); - ret_val = val; + m_value = val; break; } case coString: case coStrings: case coFloatOrPercent: - ret_val = str.ToStdString(); + m_value = str.ToStdString(); break; default: break; } - return ret_val; +// return m_value; } void TextCtrl::BUILD() { @@ -183,12 +183,12 @@ namespace Slic3r { namespace GUI { window = dynamic_cast(temp); } - boost::any TextCtrl::get_value() + boost::any& TextCtrl::get_value() { wxString ret_str = static_cast(window)->GetValue(); - boost::any ret_val = get_value_by_opt_type(ret_str); + /*boost::any ret_val*/get_value_by_opt_type(ret_str); - return ret_val; + return m_value;//ret_val; } void TextCtrl::enable() { dynamic_cast(window)->Enable(); dynamic_cast(window)->SetEditable(true); } @@ -216,15 +216,15 @@ void CheckBox::BUILD() { window = dynamic_cast(temp); } -boost::any CheckBox::get_value() +boost::any& CheckBox::get_value() { - boost::any ret_val; +// boost::any m_value; bool value = dynamic_cast(window)->GetValue(); if (m_opt.type == coBool) - ret_val = static_cast(value); + m_value = static_cast(value); else - ret_val = static_cast(value); - return ret_val; + m_value = static_cast(value); + return m_value; } int undef_spin_val = -9999; //! Probably, It's not necessary @@ -424,7 +424,33 @@ void Choice::set_value(const boost::any& value, bool change_event) break; } case coEnum:{ - dynamic_cast(window)->SetSelection(boost::any_cast(value)); + int val = boost::any_cast(value); + if (m_opt_id.compare("external_fill_pattern") == 0) + { + if (!m_opt.enum_values.empty()){ + std::string key; + t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); + for (auto it : map_names) { + if (val == it.second) { + key = it.first; + break; + } + } + + size_t idx = 0; + for (auto el : m_opt.enum_values) + { + if (el.compare(key) == 0) + break; + ++idx; + } + + val = idx == m_opt.enum_values.size() ? 0 : idx; + } + else + val = 0; + } + dynamic_cast(window)->SetSelection(val); break; } default: @@ -454,16 +480,16 @@ void Choice::set_values(const std::vector& values) m_disable_change_event = false; } -boost::any Choice::get_value() +boost::any& Choice::get_value() { - boost::any ret_val; +// boost::any m_value; wxString ret_str = static_cast(window)->GetValue(); if (m_opt_id == "support") - return ret_str; + return m_value = boost::any(ret_str);//ret_str; if (m_opt.type != coEnum) - ret_val = get_value_by_opt_type(ret_str); + /*m_value = */get_value_by_opt_type(ret_str); else { int ret_enum = static_cast(window)->GetSelection(); @@ -474,22 +500,22 @@ boost::any Choice::get_value() t_config_enum_values map_names = ConfigOptionEnum::get_enum_values(); int value = map_names.at(key); - ret_val = static_cast(value); + m_value = static_cast(value); } else - ret_val = static_cast(0); + m_value = static_cast(0); } if (m_opt_id.compare("fill_pattern") == 0) - ret_val = static_cast(ret_enum); + m_value = static_cast(ret_enum); else if (m_opt_id.compare("gcode_flavor") == 0) - ret_val = static_cast(ret_enum); + m_value = static_cast(ret_enum); else if (m_opt_id.compare("support_material_pattern") == 0) - ret_val = static_cast(ret_enum); + m_value = static_cast(ret_enum); else if (m_opt_id.compare("seam_position") == 0) - ret_val = static_cast(ret_enum); + m_value = static_cast(ret_enum); } - return ret_val; + return m_value; } void ColourPicker::BUILD() @@ -509,14 +535,14 @@ void ColourPicker::BUILD() temp->SetToolTip(get_tooltip_text(clr)); } -boost::any ColourPicker::get_value(){ - boost::any ret_val; +boost::any& ColourPicker::get_value(){ +// boost::any m_value; auto colour = static_cast(window)->GetColour(); auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); - ret_val = clr_str.ToStdString(); + m_value = clr_str.ToStdString(); - return ret_val; + return m_value; } void PointCtrl::BUILD() @@ -580,7 +606,7 @@ void PointCtrl::set_value(const boost::any& value, bool change_event) set_value(pt, change_event); } -boost::any PointCtrl::get_value() +boost::any& PointCtrl::get_value() { Pointf ret_point; double val; @@ -588,7 +614,7 @@ boost::any PointCtrl::get_value() ret_point.x = val; y_textctrl->GetValue().ToDouble(&val); ret_point.y = val; - return ret_point; + return m_value = ret_point; } } // GUI diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index cdc7c0d81..c7eb25c75 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -85,22 +85,18 @@ public: /// Gets a boost::any representing this control. /// subclasses should overload with a specific version - virtual boost::any get_value() = 0; + virtual boost::any& get_value() = 0; virtual void enable() = 0; virtual void disable() = 0; - wxStaticText* m_Label = nullptr; - wxButton* m_Undo_btn = nullptr; - wxButton* m_Undo_to_sys_btn = nullptr; - - /// Fires the enable or disable function, based on the input. + /// Fires the enable or disable function, based on the input. inline void toggle(bool en) { en ? enable() : disable(); } virtual wxString get_tooltip_text(const wxString& default_string); // set icon to "UndoToSystemValue" button according to an inheritance of preset - void set_nonsys_btn_icon(const std::string& icon); +// void set_nonsys_btn_icon(const wxBitmap& icon); Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {}; Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {}; @@ -110,7 +106,8 @@ public: virtual wxWindow* getWindow() { return nullptr; } bool is_matched(const std::string& string, const std::string& pattern); - boost::any get_value_by_opt_type(wxString& str); +// boost::any get_value_by_opt_type(wxString& str); + void get_value_by_opt_type(wxString& str); /// Factory method for generating new derived classes. template @@ -120,6 +117,71 @@ public: p->PostInitialize(); return std::move(p); //!p; } + + bool set_undo_bitmap(const wxBitmap *bmp) { + if (m_undo_bitmap != bmp) { + m_undo_bitmap = bmp; + m_Undo_btn->SetBitmap(*bmp); + return true; + } + return false; + } + + bool set_undo_to_sys_bitmap(const wxBitmap *bmp) { + if (m_undo_to_sys_bitmap != bmp) { + m_undo_to_sys_bitmap = bmp; + m_Undo_to_sys_btn->SetBitmap(*bmp); + return true; + } + return false; + } + + bool set_label_colour(const wxColour *clr) { + if (m_Label == nullptr) return false; + if (m_label_color != clr) { + m_label_color = clr; + m_Label->SetForegroundColour(*clr); + m_Label->Refresh(true); + } + return false; + } + + bool set_undo_tooltip(const wxString *tip) { + if (m_undo_tooltip != tip) { + m_undo_tooltip = tip; + m_Undo_btn->SetToolTip(*tip); + return true; + } + return false; + } + + bool set_undo_to_sys_tooltip(const wxString *tip) { + if (m_undo_to_sys_tooltip != tip) { + m_undo_to_sys_tooltip = tip; + m_Undo_to_sys_btn->SetToolTip(*tip); + return true; + } + return false; + } + +protected: + wxButton* m_Undo_btn = nullptr; + // Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. + const wxBitmap* m_undo_bitmap = nullptr; + const wxString* m_undo_tooltip = nullptr; + wxButton* m_Undo_to_sys_btn = nullptr; + // Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. + const wxBitmap* m_undo_to_sys_bitmap = nullptr; + const wxString* m_undo_to_sys_tooltip = nullptr; + + wxStaticText* m_Label = nullptr; + // Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. + const wxColour* m_label_color; + + // current value + boost::any m_value; + + friend class OptionsGroup; }; /// Convenience function, accepts a const reference to t_field and checks to see whether @@ -153,7 +215,7 @@ public: m_disable_change_event = false; } - boost::any get_value() override; + boost::any& get_value() override; virtual void enable(); virtual void disable(); @@ -180,7 +242,7 @@ public: dynamic_cast(window)->SetValue(boost::any_cast(value)); m_disable_change_event = false; } - boost::any get_value() override; + boost::any& get_value() override; void enable() override { dynamic_cast(window)->Enable(); } void disable() override { dynamic_cast(window)->Disable(); } @@ -210,8 +272,9 @@ public: dynamic_cast(window)->SetValue(tmp_value); m_disable_change_event = false; } - boost::any get_value() override { - return boost::any(tmp_value); + boost::any& get_value() override { +// return boost::any(tmp_value); + return m_value = tmp_value; } void enable() override { dynamic_cast(window)->Enable(); } @@ -233,7 +296,7 @@ public: void set_value(const std::string& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); void set_values(const std::vector &values); - boost::any get_value() override; + boost::any& get_value() override; void enable() override { dynamic_cast(window)->Enable(); }; void disable() override{ dynamic_cast(window)->Disable(); }; @@ -261,7 +324,7 @@ public: m_disable_change_event = false; } - boost::any get_value() override; + boost::any& get_value() override; void enable() override { dynamic_cast(window)->Enable(); }; void disable() override{ dynamic_cast(window)->Disable(); }; @@ -283,7 +346,7 @@ public: void set_value(const Pointf& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); - boost::any get_value() override; + boost::any& get_value() override; void enable() override { x_textctrl->Enable(); diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index bd74e118e..3e8e2740b 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -187,6 +187,7 @@ PresetBundle *g_PresetBundle= nullptr; PresetUpdater *g_PresetUpdater = nullptr; wxColour g_color_label_modified; wxColour g_color_label_sys; +wxColour g_color_label_default; std::vector g_tabs_list; @@ -206,6 +207,7 @@ static void init_label_colours() g_color_label_modified = wxColour(253, 111, 40); g_color_label_sys = wxColour(115, 220, 103); } + g_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); } void set_wxapp(wxApp *app) @@ -699,6 +701,10 @@ const wxColour& get_sys_label_clr() { return g_color_label_sys; } +const wxColour& get_default_label_clr() { + return g_color_label_default; +} + unsigned get_colour_approx_luma(const wxColour &colour) { double r = colour.Red(); diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index a8bbdccc7..d4b5988a8 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -86,6 +86,7 @@ PresetBundle* get_preset_bundle(); const wxColour& get_modified_label_clr(); const wxColour& get_sys_label_clr(); +const wxColour& get_default_label_clr(); unsigned get_colour_approx_luma(const wxColour &colour); extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change); diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index 168ffcdc9..265497e9c 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -90,8 +90,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co field->m_Undo_btn->Hide(); field->m_Undo_to_sys_btn->Hide(); } - if (nonsys_btn_icon != nullptr) - field->set_nonsys_btn_icon(nonsys_btn_icon()); +// if (nonsys_btn_icon != nullptr) +// field->set_nonsys_btn_icon(*nonsys_btn_icon); // assign function objects for callbacks, etc. return field; @@ -149,7 +149,8 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // If there's a widget, build it and add the result to the sizer. if (line.widget != nullptr) { auto wgt = line.widget(parent()); - grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5); + // If widget doesn't have label, don't use border + grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5); if (colored_Label != nullptr) *colored_Label = label; return; } diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp index dd6d48f46..6a00cbe0d 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/xs/src/slic3r/GUI/OptionsGroup.hpp @@ -1,3 +1,6 @@ +#ifndef slic3r_OptionsGroup_hpp_ +#define slic3r_OptionsGroup_hpp_ + #include #include #include @@ -86,7 +89,7 @@ public: wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; - std::function nonsys_btn_icon{ nullptr }; +// std::function nonsys_btn_icon{ nullptr }; /// Returns a copy of the pointer of the parent wxWindow. /// Accessor function is because users are not allowed to change the parent @@ -204,3 +207,5 @@ public: }; }} + +#endif /* slic3r_OptionsGroup_hpp_ */ diff --git a/xs/src/slic3r/GUI/Preferences.hpp b/xs/src/slic3r/GUI/Preferences.hpp index c30e94d22..f94a1db69 100644 --- a/xs/src/slic3r/GUI/Preferences.hpp +++ b/xs/src/slic3r/GUI/Preferences.hpp @@ -1,3 +1,6 @@ +#ifndef slic3r_Preferences_hpp_ +#define slic3r_Preferences_hpp_ + #include "GUI.hpp" #include @@ -25,3 +28,5 @@ public: } // GUI } // Slic3r + +#endif /* slic3r_Preferences_hpp_ */ diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 640fcaaaf..eca809ec1 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -3,6 +3,7 @@ #include "Preset.hpp" #include "AppConfig.hpp" +#include "BitmapCache.hpp" #include #include @@ -340,7 +341,8 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vectorFreeze(); ui->Clear(); - std::map nonsys_presets; + + const Preset &selected_preset = this->get_selected_preset(); + // Show wide icons if the currently selected preset is not compatible with the current printer, + // and draw a red flag in front of the selected preset. + bool wide_icons = !selected_preset.is_compatible && m_bitmap_incompatible != nullptr; + + std::map nonsys_presets; wxString selected = ""; - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { + if (!this->m_presets.front().is_visible) + ui->Append("------- System presets -------", wxNullBitmap); + for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) { const Preset &preset = this->m_presets[i]; if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) continue; - const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible; -// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), -// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); -// if (i == m_idx_selected) -// ui->SetSelection(ui->GetCount() - 1); + std::string bitmap_key = ""; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + if (wide_icons) + // Paint a red flag for incompatible presets. + bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(16, 16) : *m_bitmap_incompatible); + // Paint the color bars. + bmps.emplace_back(m_bitmap_cache->mkclear(4, 16)); + bmps.emplace_back(*m_bitmap_main_frame); + // Paint a lock at the system presets. + bmps.emplace_back(m_bitmap_cache->mkclear(6, 16)); + bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } if (preset.is_default || preset.is_system){ ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), @@ -605,20 +633,18 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) } else { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible); + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); if (i == m_idx_selected) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); } if (preset.is_default) - ui->Append("------------------------------------", wxNullBitmap); + ui->Append("------- System presets -------", wxNullBitmap); } if (!nonsys_presets.empty()) { - ui->Append("------------------------------------", wxNullBitmap); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(it->first, - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); + ui->Append("------- User presets -------", wxNullBitmap); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + ui->Append(it->first, *it->second); if (it->first == selected) ui->SetSelection(ui->GetCount() - 1); } @@ -626,51 +652,63 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) ui->Thaw(); } -void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) +size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) { if (ui == nullptr) - return; + return 0; ui->Freeze(); ui->Clear(); - std::map nonsys_presets; + size_t selected_preset_item = 0; + + std::map nonsys_presets; wxString selected = ""; - for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { + if (!this->m_presets.front().is_visible) + ui->Append("------- System presets -------", wxNullBitmap); + for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++i) { const Preset &preset = this->m_presets[i]; if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) continue; - const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; -// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), -// (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); -// if (i == m_idx_selected) -// ui->SetSelection(ui->GetCount() - 1); + std::string bitmap_key = "tab"; + bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; + bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; + wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); + if (bmp == nullptr) { + // Create the bitmap with color bars. + std::vector bmps; + const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; + bmps.emplace_back((tmp_bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *tmp_bmp); + // Paint a lock at the system presets. + bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16)); + bmp = m_bitmap_cache->insert(bitmap_key, bmps); + } if (preset.is_default || preset.is_system){ ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) - ui->SetSelection(ui->GetCount() - 1); + selected_preset_item = ui->GetCount() - 1; } else { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible); + nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); if (i == m_idx_selected) selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); } if (preset.is_default) - ui->Append("------------------------------------", wxNullBitmap); + ui->Append("------- System presets -------", wxNullBitmap); } if (!nonsys_presets.empty()) { - ui->Append("------------------------------------", wxNullBitmap); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(it->first, - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); + ui->Append("------- User presets -------", wxNullBitmap); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + ui->Append(it->first, *it->second); if (it->first == selected) - ui->SetSelection(ui->GetCount() - 1); + selected_preset_item = ui->GetCount() - 1; } } + ui->SetSelection(selected_preset_item); ui->Thaw(); + return selected_preset_item; } // Update a dirty floag of the current preset, update the labels of the UI component accordingly. @@ -703,11 +741,13 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) return was_dirty != is_dirty; } -std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference) +std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type /*= false*/) { std::vector changed; - if (edited != nullptr && reference != nullptr) { - changed = reference->config.diff(edited->config); + if (edited != nullptr && reference != nullptr) { + changed = is_printer_type ? + reference->config.deep_diff(edited->config) : + reference->config.diff(edited->config); // The "compatible_printers" option key is handled differently from the others: // It is not mandatory. If the key is missing, it means it is compatible with any printer. // If the key exists and it is empty, it means it is compatible with no printer. @@ -764,6 +804,11 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b // If the first visible preset was not found, return the 0th element, which is the default preset. } + // Temporary decision + if (name_w_suffix == "------- System presets -------" || + name_w_suffix == "------- User presets -------") + return true; + // 2) Select the new preset. if (m_idx_selected != idx || force) { this->select_preset(idx); diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index e80f138ee..ab01a48ff 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -20,6 +20,10 @@ namespace Slic3r { class AppConfig; class PresetBundle; +namespace GUI { + class BitmapCache; +} + enum ConfigFileType { CONFIG_FILE_TYPE_UNKNOWN, @@ -302,18 +306,18 @@ public: // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. bool current_is_dirty() const { return ! this->current_dirty_options().empty(); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector current_dirty_options() const - { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset()); } + std::vector current_dirty_options(const bool is_printer_type = false) const + { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), is_printer_type); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector current_different_from_parent_options() const - { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent()); } + std::vector current_different_from_parent_options(const bool is_printer_type = false) const + { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); } // Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal. std::vector system_equal_options() const; // Update the choice UI from the list of presets. // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. - void update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); + size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); // Update the choice UI from the list of presets. // Only the compatible presets are shown. // If an incompatible preset is selected, it is shown as well. @@ -358,7 +362,7 @@ private: size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible); - static std::vector dirty_options(const Preset *edited, const Preset *reference); + static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false); // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. Preset::Type m_type; @@ -384,6 +388,9 @@ private: // Path to the directory to store the config files into. std::string m_dir_path; + // Caching color bitmaps for the filament combo box. + GUI::BitmapCache *m_bitmap_cache = nullptr; + // to access select_preset_by_name_strict() friend class PresetBundle; }; diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 717e7a6f0..9123e144b 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -1011,6 +1011,10 @@ void PresetBundle::export_configbundle(const std::string &path) //, const Dynami // an optional "(modified)" suffix will be removed from the filament name. void PresetBundle::set_filament_preset(size_t idx, const std::string &name) { + if (name == "------- System presets -------" || + name == "------- User presets -------") + return; + if (idx >= filament_presets.size()) filament_presets.resize(idx + 1, filaments.default_preset().name); filament_presets[idx] = Preset::remove_suffix_modified(name); @@ -1059,9 +1063,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma // and draw a red flag in front of the selected preset. bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; assert(selected_preset != nullptr); - std::map nonsys_presets; + std::map nonsys_presets; wxString selected_str = ""; - for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) { + if (!this->filaments().front().is_visible) + ui->Append("------- System presets -------", wxNullBitmap); + for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { const Preset &preset = this->filaments.preset(i); bool selected = this->filament_presets[idx_extruder] == preset.name; if (! preset.is_visible || (! preset.is_compatible && ! selected)) @@ -1093,14 +1099,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb)); } // Paint a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(4, 16)); - bmps.emplace_back((preset.is_system || preset.is_default) ? - (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); + bmps.emplace_back(m_bitmapCache->mkclear(2, 16)); + bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(16, 16)); +// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); bitmap = m_bitmapCache->insert(bitmap_key, bmps); } -// ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); -// if (selected) -// ui->SetSelection(ui->GetCount() - 1); if (preset.is_default || preset.is_system){ ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), @@ -1111,19 +1114,19 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma else { nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? wxNullBitmap : *bitmap); + (bitmap == 0) ? &wxNullBitmap : bitmap); if (selected) selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); } if (preset.is_default) - ui->Append("------------------------------------", wxNullBitmap); + ui->Append("------- System presets -------", wxNullBitmap); } if (!nonsys_presets.empty()) { - ui->Append("------------------------------------", wxNullBitmap); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, it->second); + ui->Append("------- User presets -------", wxNullBitmap); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + ui->Append(it->first, *it->second); if (it->first == selected_str) ui->SetSelection(ui->GetCount() - 1); } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index b7aaa78b7..4b2ecab60 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -8,6 +8,7 @@ #include "slic3r/Utils/OctoPrint.hpp" #include "BonjourDialog.hpp" #include "WipeTowerDialog.hpp" +#include "ButtonsDescription.hpp" #include #include @@ -23,6 +24,9 @@ #include #include "wxExtensions.hpp" +#include + +#include namespace Slic3r { namespace GUI { @@ -88,9 +92,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) 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); + m_bmp_show_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG); + m_bmp_hide_incompatible_presets.LoadFile(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); @@ -99,14 +103,44 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) m_undo_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); m_undo_to_sys_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); + m_question_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); if (wxMSW) { m_undo_btn->SetBackgroundColour(color); m_undo_to_sys_btn->SetBackgroundColour(color); + m_question_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(); })); - m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG)); - m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); })); + + m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information."))); + + // Determine the theme color of OS (dark or light) + auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. + m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG); + m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG); + m_bmp_non_system = &m_bmp_white_bullet; + // Bitmaps to be shown on the "Undo user changes" button next to each input field. + m_bmp_value_revert .LoadFile(from_u8(var(luma >= 128 ? "action_undo.png" : "action_undo_grey.png")), wxBITMAP_TYPE_PNG); + m_bmp_white_bullet .LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); + m_bmp_question .LoadFile(from_u8(var("question_mark_01.png")), wxBITMAP_TYPE_PNG); + + fill_icon_descriptions(); + set_tooltips_text(); + + m_undo_btn->SetBitmap(m_bmp_white_bullet); + m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); })); + m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet); + m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); })); + m_question_btn->SetBitmap(m_bmp_question); + m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) + { + auto dlg = new ButtonsDescription(this, &m_icon_descriptions); + dlg->ShowModal(); + })); + + // Colors for ui "decoration" + m_sys_label_clr = get_sys_label_clr(); + m_modified_label_clr = get_modified_label_clr(); + m_default_text_clr = get_default_label_clr(); m_hsizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_hsizer, 0, wxBOTTOM, 3); @@ -120,7 +154,8 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) m_hsizer->AddSpacer(64); m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); -// m_hsizer->AddSpacer(64); + m_hsizer->AddSpacer(32); + m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); // m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); //Horizontal sizer to hold the tree and the selected page. @@ -183,8 +218,16 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) //! select_preset(m_presets_choice->GetStringSelection().ToStdString()); //! we doing next: int selected_item = m_presets_choice->GetSelection(); + if (m_selected_preset_item == selected_item) + return; if (selected_item >= 0){ std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); + if (selected_string == "------- System presets -------" || + selected_string == "------- User presets -------"){ + m_presets_choice->SetSelection(m_selected_preset_item); + return; + } + m_selected_preset_item = selected_item; select_preset(selected_string); } })); @@ -204,8 +247,9 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle) void Tab::load_initial_data() { m_config = &m_presets->get_edited_preset().config; - m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ? - "bullet_white.png" : "sys_unlock.png"; + m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; + m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; + m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; } PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/) @@ -234,140 +278,78 @@ PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bo 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)); - } -} - -template -void add_correct_opts_to_sys_options(const std::string &opt_key, std::vector *vec, TabPrinter *tab) -{ - const Preset* sys_preset = tab->m_presets->get_selected_preset_parent(); - if (sys_preset == nullptr) - return; - T *opt_cur = static_cast(tab->m_config->option(opt_key)); - const T *opt_sys = static_cast(sys_preset->config.option(opt_key)); - int opt_max_id = opt_sys->values.size()-1; - for (int i = 0; i < opt_cur->values.size(); i++) - { - int init_id = i <= opt_max_id ? i : 0; - if (opt_cur->values[i] == opt_sys->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 (m_postpone_update_ui) + return; - if (name() == "printer"){ - // Update dirty_options in case changes of Extruder's options + const bool is_printer_type = (name() == "printer"); + auto dirty_options = m_presets->current_dirty_options(is_printer_type); + auto nonsys_options = m_presets->current_different_from_parent_options(is_printer_type); + if (is_printer_type){ TabPrinter* tab = static_cast(this); - m_dirty_options.resize(0); - for (auto opt_key : dirty_options) - { - if (opt_key == "bed_shape"){ m_dirty_options.emplace_back(opt_key); continue; } - switch (m_config->option(opt_key)->type()) - { - case coInts: add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - case coBools: add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - case coFloats: add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - case coStrings: add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - case coPercents:add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - case coPoints: add_correct_opts_to_dirty_options(opt_key, &m_dirty_options, tab); break; - default: m_dirty_options.emplace_back(opt_key); break; - } - } if (tab->m_initial_extruders_count != tab->m_extruders_count) - m_dirty_options.emplace_back("extruders_count"); - - m_sys_options.resize(0); - const auto sys_preset = m_presets->get_selected_preset_parent(); - if (sys_preset){ - for (auto opt_key : m_config->keys()) - { - if (opt_key == "bed_shape"){ - if (*tab->m_config->option(opt_key) == *sys_preset->config.option(opt_key)) - m_sys_options.emplace_back(opt_key); - continue; - } - switch (m_config->option(opt_key)->type()) - { - case coInts: add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - case coBools: add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - case coFloats: add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - case coStrings: add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - case coPercents:add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - case coPoints: add_correct_opts_to_sys_options(opt_key, &m_sys_options, tab); break; - default:{ - const ConfigOption *opt_cur = tab->m_config->option(opt_key); - const ConfigOption *opt_sys = sys_preset->config.option(opt_key); - if (opt_cur != nullptr && opt_sys != nullptr && *opt_cur == *opt_sys) - m_sys_options.emplace_back(opt_key); - break; - } - } - } - - if (tab->m_sys_extruders_count == tab->m_extruders_count) - m_sys_options.emplace_back("extruders_count"); - } - } - else{ - m_sys_options = m_presets->system_equal_options(); - m_dirty_options = dirty_options; + dirty_options.emplace_back("extruders_count"); + if (tab->m_sys_extruders_count != tab->m_extruders_count) + nonsys_options.emplace_back("extruders_count"); } + for (auto& it : m_options_list) + it.second = m_opt_status_value; + + for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue; + for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue; + Freeze(); //update options "decoration" - for (const auto opt_key : m_full_options_list) + for (const auto opt : m_options_list) { bool is_nonsys_value = false; bool is_modified_value = true; - std::string sys_icon = "sys_lock.png"; - std::string icon = "action_undo.png"; - wxColour color = get_sys_label_clr(); - if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) { + const wxBitmap *sys_icon = &m_bmp_value_lock; + const wxBitmap *icon = &m_bmp_value_revert; + + const wxColour *color = &m_sys_label_clr; + + const wxString *sys_tt = &m_tt_value_lock; + const wxString *tt = &m_tt_value_revert; + + // value isn't equal to system value + if ((opt.second & osSystemValue) == 0){ is_nonsys_value = true; - sys_icon = m_nonsys_btn_icon; - if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) - color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + sys_icon = m_bmp_non_system; + sys_tt = m_tt_non_system; + // value is equal to last saved + if ((opt.second & osInitValue) != 0) + color = &m_default_text_clr; + // value is modified else - color = get_modified_label_clr(); + color = &m_modified_label_clr; } - if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) + if ((opt.second & osInitValue) != 0) { is_modified_value = false; - icon = "bullet_white.png"; + icon = &m_bmp_white_bullet; + tt = &m_tt_white_bullet; } - if (opt_key == "bed_shape" || opt_key == "compatible_printers") { + if (opt.first == "bed_shape" || opt.first == "compatible_printers") { if (m_colored_Label != nullptr) { - m_colored_Label->SetForegroundColour(color); + m_colored_Label->SetForegroundColour(*color); m_colored_Label->Refresh(true); } continue; } - Field* field = get_field(opt_key); + Field* field = get_field(opt.first); if (field == nullptr) continue; field->m_is_nonsys_value = is_nonsys_value; field->m_is_modified_value = is_modified_value; - field->m_Undo_btn->SetBitmap(wxBitmap(from_u8(var(icon)), wxBITMAP_TYPE_PNG)); - field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(sys_icon)), wxBITMAP_TYPE_PNG)); - if (field->m_Label != nullptr){ - field->m_Label->SetForegroundColour(color); - field->m_Label->Refresh(true); - } + field->set_undo_bitmap(icon); + field->set_undo_to_sys_bitmap(sys_icon); + field->set_undo_tooltip(tt); + field->set_undo_to_sys_tooltip(sys_tt); + field->set_label_colour(color); } Thaw(); @@ -376,67 +358,53 @@ void Tab::update_changed_ui() }); } +void Tab::init_options_list() +{ + if (!m_options_list.empty()) + m_options_list.clear(); + + for (const auto opt_key : m_config->keys()) + m_options_list.emplace(opt_key, m_opt_status_value); +} + template -void add_correct_opts_to_full_options_list(const std::string &opt_key, std::vector *vec, TabPrinter *tab) +void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, TabPrinter *tab, const int& value) { T *opt_cur = static_cast(tab->m_config->option(opt_key)); for (int i = 0; i < opt_cur->values.size(); i++) - vec->emplace_back(opt_key + "#" + std::to_string(i)); + map.emplace(opt_key + "#" + std::to_string(i), value); } -void Tab::update_full_options_list() +void TabPrinter::init_options_list() { - if (!m_full_options_list.empty()) - m_full_options_list.resize(0); + if (!m_options_list.empty()) + m_options_list.clear(); - if (m_name != "printer"){ - m_full_options_list = m_config->keys(); - return; - } - - TabPrinter* tab = static_cast(this); for (const auto opt_key : m_config->keys()) { if (opt_key == "bed_shape"){ - m_full_options_list.emplace_back(opt_key); + m_options_list.emplace(opt_key, m_opt_status_value); continue; } switch (m_config->option(opt_key)->type()) { - case coInts: add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - case coBools: add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - case coFloats: add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - case coStrings: add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - case coPercents:add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - case coPoints: add_correct_opts_to_full_options_list(opt_key, &m_full_options_list, tab); break; - default: m_full_options_list.emplace_back(opt_key); break; + case coInts: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coBools: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coFloats: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coStrings: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, m_opt_status_value); break; } } - m_full_options_list.emplace_back("extruders_count"); -} - -void Tab::update_sys_ui_after_sel_preset() -{ - for (const auto opt_key : m_full_options_list){ - Field* field = get_field(opt_key); - if (field != nullptr){ - field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG)); - field->m_is_nonsys_value = true; - if (field->m_Label != nullptr){ - field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - field->m_Label->Refresh(true); - } - } - } - m_sys_options.resize(0); + m_options_list.emplace("extruders_count", m_opt_status_value); } void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page) { - if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) - sys_page = false; - if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end()) - modified_page = true; + auto opt = m_options_list.find(opt_key); + if (sys_page) sys_page = (opt->second & osSystemValue) != 0; + if (!modified_page) modified_page = (opt->second & osInitValue) == 0; } void Tab::update_changed_tree_ui() @@ -457,7 +425,7 @@ void Tab::update_changed_tree_ui() get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } - if (title == _("Dependencies")){ + if (title == _("Dependencies") && name() != "printer"){ get_sys_and_mod_flags("compatible_printers", sys_page, modified_page); } for (auto group : page->m_optgroups) @@ -469,12 +437,13 @@ void Tab::update_changed_tree_ui() get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } - if (sys_page) - m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr()); - else if (modified_page) - m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr()); - else - m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + + const wxColor *clr = sys_page ? &m_sys_label_clr : + modified_page ? &m_modified_label_clr : + &m_default_text_clr; + + if (page->set_item_colour(clr)) + m_treectrl->SetItemTextColour(cur_item, *clr); page->m_is_nonsys_values = !sys_page; page->m_is_modified_values = modified_page; @@ -493,80 +462,62 @@ void Tab::update_changed_tree_ui() void Tab::update_undo_buttons() { - const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" : "action_undo.png"; - const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon : "sys_lock.png"; + m_undo_btn->SetBitmap(m_is_modified_values ? m_bmp_value_revert : m_bmp_white_bullet); + m_undo_to_sys_btn->SetBitmap(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); - m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG)); - m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG)); + m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet); + m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock); } -void Tab::on_back_to_initial_value() +void Tab::on_roll_back_value(const bool to_sys /*= true*/) { - if (!m_is_modified_values) return; + int os; + if (to_sys) { + if (!m_is_nonsys_values) return; + os = osSystemValue; + } + else { + if (!m_is_modified_values) return; + os = osInitValue; + } + + m_postpone_update_ui = true; auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); for (auto page : m_pages) if (page->title() == selection) { for (auto group : page->m_optgroups){ if (group->title == _("Capabilities")){ - if (find(m_dirty_options.begin(), m_dirty_options.end(), "extruders_count") != m_dirty_options.end()) - group->back_to_initial_value("extruders_count"); + if ((m_options_list["extruders_count"] & os) == 0) + to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); } if (group->title == _("Size and coordinates")){ - if (find(m_dirty_options.begin(), m_dirty_options.end(), "bed_shape") != m_dirty_options.end()) - group->back_to_initial_value("bed_shape"); - } - if (group->title == _("Profile dependencies")){ - if (find(m_dirty_options.begin(), m_dirty_options.end(), "compatible_printers") != m_dirty_options.end()) - group->back_to_initial_value("compatible_printers"); + if ((m_options_list["bed_shape"] & os) == 0){ + to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); + load_key_value("bed_shape", true/*some value*/, true); + } - bool is_empty = m_config->option("compatible_printers")->values.empty(); - m_compatible_printers_checkbox->SetValue(is_empty); - is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable(); + } + if (group->title == _("Profile dependencies") && name() != "printer"){ + if ((m_options_list["compatible_printers"] & os) == 0){ + to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); + load_key_value("compatible_printers", true/*some value*/, true); + + bool is_empty = m_config->option("compatible_printers")->values.empty(); + m_compatible_printers_checkbox->SetValue(is_empty); + is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable(); + } } for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { const std::string& opt_key = it->first; - if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end()) - group->back_to_initial_value(opt_key); + if ((m_options_list[opt_key] & os) == 0) + to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); } } break; } - update_changed_ui(); -} -void Tab::on_back_to_sys_value() -{ - if (!m_is_nonsys_values) return; - - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - for (auto page : m_pages) - if (page->title() == selection) { - for (auto group : page->m_optgroups) { - if (group->title == _("Capabilities")){ - if (find(m_sys_options.begin(), m_sys_options.end(), "extruders_count") == m_sys_options.end()) - group->back_to_sys_value("extruders_count"); - } - if (group->title == _("Size and coordinates")){ - if (find(m_sys_options.begin(), m_sys_options.end(), "bed_shape") == m_sys_options.end()) - group->back_to_sys_value("bed_shape"); - } - if (group->title == _("Profile dependencies")){ - if (find(m_sys_options.begin(), m_sys_options.end(), "compatible_printers") == m_sys_options.end()) - group->back_to_sys_value("compatible_printers"); - - bool is_empty = m_config->option("compatible_printers")->values.empty(); - m_compatible_printers_checkbox->SetValue(is_empty); - is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable(); - } - for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { - const std::string& opt_key = it->first; - if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) - group->back_to_sys_value(opt_key); - } - } - break; - } + m_postpone_update_ui = false; update_changed_ui(); } @@ -581,7 +532,7 @@ void Tab::update_dirty(){ void Tab::update_tab_ui() { - m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); + m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); // update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets); // update_presetsctrl(m_presetctrl, m_show_incompatible_presets); } @@ -636,9 +587,11 @@ bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value) // To be called by custom widgets, load a value into a config, // update the preset selection boxes (the dirty flags) -void Tab::load_key_value(const std::string& opt_key, const boost::any& value) +// If value is saved before calling this function, put saved_value = true, +// and value can be some random value because in this case it will not been used +void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value /*= false*/) { - change_opt_value(*m_config, opt_key, value); + if (!saved_value) change_opt_value(*m_config, opt_key, value); // Mark the print & filament enabled if they are compatible with the currently selected preset. if (opt_key.compare("compatible_printers") == 0) { // Don't select another profile if this profile happens to become incompatible. @@ -1354,6 +1307,7 @@ void TabFilament::reload_config(){ void TabFilament::update() { + Freeze(); wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); m_cooling_description_line->SetText(text); text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle)); @@ -1367,6 +1321,7 @@ void TabFilament::update() for (auto el : { "min_fan_speed", "disable_fan_first_layers" }) get_field(el)->toggle(fan_always_on); + Thaw(); } void TabFilament::OnActivate() @@ -1396,6 +1351,9 @@ void TabPrinter::build() m_presets = &m_preset_bundle->printers; load_initial_data(); + // to avoid redundant memory allocation / deallocation during extruders count changing + m_pages.reserve(30); + auto *nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); const Preset* parent_preset = m_presets->get_selected_preset_parent(); @@ -1673,12 +1631,34 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){ } void TabPrinter::build_extruder_pages(){ - for (auto extruder_idx = m_extruder_pages.size(); extruder_idx < m_extruders_count; ++extruder_idx){ + size_t n_before_extruders = 2; // Count of pages before Extruder pages + size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page + + if (m_extruders_count_old == m_extruders_count || m_extruders_count <= 2) + { + // if we have a single extruder MM setup, add a page with configuration options: + for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already + if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) { + m_pages.erase(m_pages.begin() + i); + break; + } + if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material")) { + // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves + auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true); + auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters"))); + optgroup->append_single_option_line("cooling_tube_retraction"); + optgroup->append_single_option_line("cooling_tube_length"); + optgroup->append_single_option_line("parking_pos_retraction"); + m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page); + } + } + + for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){ //# build page char buf[MIN_BUF_LENGTH_FOR_L]; sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1); auto page = add_options_page(from_u8(buf), "funnel.png", true); - m_extruder_pages.push_back(page); + m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page); auto optgroup = page->new_optgroup(_(L("Size"))); optgroup->append_single_option_line("nozzle_diameter", extruder_idx); @@ -1716,36 +1696,11 @@ void TabPrinter::build_extruder_pages(){ } // # remove extra pages - if (m_extruders_count <= m_extruder_pages.size()) { - m_extruder_pages.resize(m_extruders_count); - } + if (m_extruders_count < m_extruders_count_old) + m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count, + m_pages.begin() + n_before_extruders + m_extruders_count_old); - // # rebuild page list - PageShp page_note = m_pages.back(); - m_pages.pop_back(); - while (m_pages.back()->title().find(_(L("Extruder"))) != std::string::npos) - m_pages.pop_back(); - for (auto page_extruder : m_extruder_pages) - m_pages.push_back(page_extruder); - m_pages.push_back(page_note); - - { - // if we have a single extruder MM setup, add a page with configuration options: - for (int i=0;ititle().find(_(L("Single extruder MM setup"))) != std::string::npos) { - m_pages.erase(m_pages.begin()+i); - break; - } - if ( m_extruder_pages.size()>1 && m_config->opt_bool("single_extruder_multi_material")) { - // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves - auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png",true); - auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters"))); - optgroup->append_single_option_line("cooling_tube_retraction"); - optgroup->append_single_option_line("cooling_tube_length"); - optgroup->append_single_option_line("parking_pos_retraction"); - m_pages.insert(m_pages.begin()+1,page); - } - } + m_extruders_count_old = m_extruders_count; rebuild_page_tree(); } @@ -1853,8 +1808,9 @@ void Tab::load_current_preset() on_preset_loaded(); // Reload preset pages with the new configuration values. reload_config(); - const Preset* parent = m_presets->get_selected_preset_parent(); - m_nonsys_btn_icon = parent == nullptr ? "bullet_white.png" : "sys_unlock.png"; + m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; + m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; + m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; // 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 @@ -1875,8 +1831,8 @@ void Tab::load_current_preset() static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); } - update_sys_ui_after_sel_preset(); - update_full_options_list(); + m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue; + init_options_list(); update_changed_ui(); }); } @@ -1888,11 +1844,13 @@ void Tab::rebuild_page_tree() // get label of the currently selected item auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection()); auto rootItem = m_treectrl->GetRootItem(); - m_treectrl->DeleteChildren(rootItem); + auto have_selection = 0; + m_treectrl->DeleteChildren(rootItem); for (auto p : m_pages) { auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); + m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); if (p->title() == selected) { m_disable_tree_sel_changed_event = 1; m_treectrl->SelectItem(itemId); @@ -1900,7 +1858,7 @@ void Tab::rebuild_page_tree() have_selection = 1; } } - + if (!have_selection) { // this is triggered on first load, so we don't disable the sel change event m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem)); @@ -2016,6 +1974,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr void Tab::OnTreeSelChange(wxTreeEvent& event) { if (m_disable_tree_sel_changed_event) return; + wxWindowUpdateLocker noUpdates(this); + Page* page = nullptr; auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); for (auto p : m_pages) @@ -2142,7 +2102,7 @@ void Tab::toggle_show_hide_incompatible() void Tab::update_show_hide_incompatible_button() { m_btn_hide_incompatible_presets->SetBitmap(m_show_incompatible_presets ? - *m_bmp_show_incompatible_presets : *m_bmp_hide_incompatible_presets); + m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets); m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ? "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); @@ -2402,6 +2362,75 @@ void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible) ui->Thaw(); } +void Tab::fill_icon_descriptions() +{ + m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_lock, L("LOCKED LOCK;" + "indicates that the settings are the same as the system values for the current option group"))); + + m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_unlock, L("UNLOCKED LOCK;" + "indicates that some settings were changed and are not equal to the system values for " + "the current option group.\n" + "Click the UNLOCKED LOCK icon to reset all settings for current option group to " + "the system values."))); + + m_icon_descriptions.push_back(t_icon_description(&m_bmp_white_bullet, L("WHITE BULLET;" + "for the left button: \tindicates a non-system preset,\n" + "for the right button: \tindicates that the settings hasn't been modified."))); + + m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_revert, L("BACK ARROW;" + "indicates that the settings were changed and are not equal to the last saved preset for " + "the current option group.\n" + "Click the BACK ARROW icon to reset all settings for the current option group to " + "the last saved preset."))); +} + +void Tab::set_tooltips_text() +{ +// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values " +// "for the current option group.\n" +// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal " +// "to the system values for the current option group.\n" +// "WHITE BULLET icon indicates a non system preset.\n\n" +// "Click the UNLOCKED LOCK icon to reset all settings for current option group to " +// "the system values."))); +// +// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved" +// "preset for the current option group.\n" +// "BACK ARROW icon indicates that the settings were changed and are not equal to " +// "the last saved preset for the current option group.\n\n" +// "Click the BACK ARROW icon to reset all settings for the current option group to " +// "the last saved preset."))); + + // --- Tooltip text for reset buttons (for whole options group) + // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. + m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values " + "for the current option group")); + m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal " + "to the system values for the current option group.\n" + "Click to reset all settings for current option group to the system values.")); + m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset.")); + m_ttg_non_system = &m_ttg_white_bullet_ns; + // Text to be shown on the "Undo user changes" button next to each input field. + m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved " + "preset for the current option group.")); + m_ttg_value_revert = _(L("BACK ARROW icon indicates that the settings were changed and are not equal to " + "the last saved preset for the current option group.\n" + "Click to reset all settings for the current option group to the last saved preset.")); + + // --- Tooltip text for reset buttons (for each option in group) + // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. + m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value.")); + m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal " + "to the system value.\n" + "Click to reset current value to the system value.")); + // m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset.")); + m_tt_non_system = &m_ttg_white_bullet_ns; + // Text to be shown on the "Undo user changes" button next to each input field. + m_tt_white_bullet = _(L("WHITE BULLET icon indicates that the value is the same as in the last saved preset.")); + m_tt_value_revert = _(L("BACK ARROW icon indicates that the value was changed and is not equal to the last saved preset.\n" + "Click to reset current value to the last saved preset.")); +} + void Page::reload_config() { for (auto group : m_optgroups) @@ -2460,10 +2489,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la return static_cast(GetParent())->m_presets->get_selected_preset_parent() != nullptr; }; - optgroup->nonsys_btn_icon = [this](){ - return static_cast(GetParent())->m_nonsys_btn_icon; - }; - 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 f9ee4f363..de04fc4cd 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -1,3 +1,6 @@ +#ifndef slic3r_Tab_hpp_ +#define slic3r_Tab_hpp_ + // The "Expert" tab at the right of the main tabbed window. // // This file implements following packages: @@ -34,6 +37,9 @@ namespace Slic3r { namespace GUI { +typedef std::pair t_icon_description; +typedef std::vector> t_icon_descriptions; + // Single Tab page containing a{ vsizer } of{ optgroups } // package Slic3r::GUI::Tab::Page; using ConfigOptionsGroupShp = std::shared_ptr; @@ -51,6 +57,7 @@ public: { Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_vsizer = new wxBoxSizer(wxVERTICAL); + m_item_color = &get_default_label_clr(); SetSizer(m_vsizer); } ~Page(){} @@ -71,6 +78,22 @@ public: Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; bool set_value(const t_config_option_key& opt_key, const boost::any& value); ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1); + + bool set_item_colour(const wxColour *clr) { + if (m_item_color != clr) { + m_item_color = clr; + return true; + } + return false; + } + + const wxColour get_item_colour() { + return *m_item_color; + } + +protected: + // Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. + const wxColour* m_item_color; }; // Slic3r::GUI::Tab; @@ -85,8 +108,6 @@ protected: wxBitmapComboBox* m_presets_choice; wxBitmapButton* m_btn_save_preset; wxBitmapButton* m_btn_delete_preset; - wxBitmap* m_bmp_show_incompatible_presets; - wxBitmap* m_bmp_hide_incompatible_presets; wxBitmapButton* m_btn_hide_incompatible_presets; wxBoxSizer* m_hsizer; wxBoxSizer* m_left_sizer; @@ -96,10 +117,51 @@ protected: wxButton* m_compatible_printers_btn; wxButton* m_undo_btn; wxButton* m_undo_to_sys_btn; + wxButton* m_question_btn; + wxComboCtrl* m_cc_presets_choice; wxDataViewTreeCtrl* m_presetctrl; wxImageList* m_preset_icons; + // Cached bitmaps. + // A "flag" icon to be displayned next to the preset name in the Tab's combo box. + wxBitmap m_bmp_show_incompatible_presets; + wxBitmap m_bmp_hide_incompatible_presets; + // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. + wxBitmap m_bmp_value_lock; + wxBitmap m_bmp_value_unlock; + wxBitmap m_bmp_white_bullet; + // The following bitmap points to either m_bmp_value_unlock or m_bmp_white_bullet, depending on whether the current preset has a parent preset. + wxBitmap *m_bmp_non_system; + // Bitmaps to be shown on the "Undo user changes" button next to each input field. + wxBitmap m_bmp_value_revert; +// wxBitmap m_bmp_value_unmodified; + wxBitmap m_bmp_question; + + // Colors for ui "decoration" + wxColour m_sys_label_clr; + wxColour m_modified_label_clr; + wxColour m_default_text_clr; + + // Tooltip text for reset buttons (for whole options group) + wxString m_ttg_value_lock; + wxString m_ttg_value_unlock; + wxString m_ttg_white_bullet_ns; + // The following text points to either m_ttg_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset. + wxString *m_ttg_non_system; + // Tooltip text to be shown on the "Undo user changes" button next to each input field. + wxString m_ttg_white_bullet; + wxString m_ttg_value_revert; + + // Tooltip text for reset buttons (for each option in group) + wxString m_tt_value_lock; + wxString m_tt_value_unlock; + // The following text points to either m_tt_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset. + wxString *m_tt_non_system; + // Tooltip text to be shown on the "Undo user changes" button next to each input field. + wxString m_tt_white_bullet; + wxString m_tt_value_revert; + int m_icon_count; std::map m_icon_index; // Map from an icon file name to its index std::vector m_pages; @@ -108,9 +170,11 @@ protected: bool m_no_controller; std::vector m_reload_dependent_tabs = {}; - std::vector m_dirty_options = {}; - std::vector m_sys_options = {}; - std::vector m_full_options_list = {}; + enum OptStatus { osSystemValue = 1, osInitValue = 2 }; + std::map m_options_list; + int m_opt_status_value; + + t_icon_descriptions m_icon_descriptions = {}; // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. wxEventType m_event_value_change = 0; @@ -118,13 +182,15 @@ protected: bool m_is_modified_values{ false }; bool m_is_nonsys_values{ true }; + bool m_postpone_update_ui {false}; + + size_t m_selected_preset_item{ 0 }; public: PresetBundle* m_preset_bundle; bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; - std::string m_nonsys_btn_icon; ogStaticText* m_parent_preset_description_line; wxStaticText* m_colored_Label = nullptr; @@ -135,7 +201,9 @@ public: Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); get_tabs_list().push_back(this); } - ~Tab() { delete_tab_from_list(this); } + ~Tab(){ + delete_tab_from_list(this); + } wxWindow* parent() const { return m_parent; } wxString title() const { return m_title; } @@ -153,7 +221,7 @@ public: wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn); void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible); - void load_key_value(const std::string& opt_key, const boost::any& value); + void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false); void reload_compatible_printers_widget(); void OnTreeSelChange(wxTreeEvent& event); @@ -165,14 +233,11 @@ public: void update_show_hide_incompatible_button(); void update_ui_from_settings(); void update_changed_ui(); - void update_full_options_list(); - void update_sys_ui_after_sel_preset(); void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page); void update_changed_tree_ui(); void update_undo_buttons(); - void on_back_to_initial_value(); - void on_back_to_sys_value(); + void on_roll_back_value(const bool to_sys = false); PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false); @@ -180,6 +245,7 @@ public: virtual void on_preset_loaded(){} virtual void build() = 0; virtual void update() = 0; + virtual void init_options_list(); void load_initial_data(); void update_dirty(); void update_tab_ui(); @@ -189,12 +255,11 @@ public: bool set_value(const t_config_option_key& opt_key, const boost::any& value); wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); bool current_preset_is_dirty(); + DynamicPrintConfig* get_config() { return m_config; } - PresetCollection* get_presets() - { - return m_presets; - } + PresetCollection* get_presets() { return m_presets; } std::vector get_dependent_tabs() { return m_reload_dependent_tabs; } + size_t get_selected_preset_item() { return m_selected_preset_item; } void on_value_change(const std::string& opt_key, const boost::any& value); @@ -203,6 +268,8 @@ protected: void update_frequently_changed_parameters(); void update_wiping_button_visibility(); void update_tab_presets(wxComboCtrl* ui, bool show_incompatible); + void fill_icon_descriptions(); + void set_tooltips_text(); }; //Slic3r::GUI::Tab::Print; @@ -248,9 +315,9 @@ public: wxButton* m_octoprint_host_test_btn; size_t m_extruders_count; + size_t m_extruders_count_old = 0; size_t m_initial_extruders_count; size_t m_sys_extruders_count; - std::vector m_extruder_pages; TabPrinter() {} TabPrinter(wxNotebook* parent, bool no_controller) : Tab(parent, _(L("Printer Settings")), "printer", no_controller) {} @@ -262,6 +329,7 @@ public: void extruders_count_changed(size_t extruders_count); void build_extruder_pages(); void on_preset_loaded() override; + void init_options_list() override; }; class SavePresetWindow :public wxDialog @@ -280,3 +348,5 @@ public: } // GUI } // Slic3r + +#endif /* slic3r_Tab_hpp_ */ diff --git a/xs/src/slic3r/GUI/TabIface.cpp b/xs/src/slic3r/GUI/TabIface.cpp index 4df0e72e5..29833322b 100644 --- a/xs/src/slic3r/GUI/TabIface.cpp +++ b/xs/src/slic3r/GUI/TabIface.cpp @@ -11,6 +11,7 @@ void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*con void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast(value)); } bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();} void TabIface::OnActivate() { return m_tab->OnActivate();} +size_t TabIface::get_selected_preset_item() { return m_tab->get_selected_preset_item(); } std::string TabIface::title() { return m_tab->title().ToUTF8().data(); } DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); } PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; } diff --git a/xs/src/slic3r/GUI/TabIface.hpp b/xs/src/slic3r/GUI/TabIface.hpp index 0325c855c..2f7f4e8e7 100644 --- a/xs/src/slic3r/GUI/TabIface.hpp +++ b/xs/src/slic3r/GUI/TabIface.hpp @@ -1,3 +1,6 @@ +#ifndef slic3r_TabIface_hpp_ +#define slic3r_TabIface_hpp_ + #include #include @@ -27,9 +30,12 @@ public: DynamicPrintConfig* get_config(); PresetCollection* get_presets(); std::vector get_dependent_tabs(); + size_t get_selected_preset_item(); protected: GUI::Tab *m_tab; -}; +}; // namespace GUI }; // namespace Slic3r + +#endif /* slic3r_TabIface_hpp_ */ diff --git a/xs/xsp/GUI_Tab.xsp b/xs/xsp/GUI_Tab.xsp index 9cacac74f..bcbdc0d9f 100644 --- a/xs/xsp/GUI_Tab.xsp +++ b/xs/xsp/GUI_Tab.xsp @@ -16,6 +16,7 @@ bool current_preset_is_dirty(); void load_key_value(char* opt_key, char* value); void OnActivate(); + size_t get_selected_preset_item(); std::string title(); Ref get_config(); Ref get_presets();