From 3cf2914a9e574691ef9fe24f4ba1a735305a2bb2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 30 Jul 2020 16:16:56 +0200 Subject: [PATCH 01/25] UnsavedChangesDialog: first implementation --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_App.hpp | 6 + src/slic3r/GUI/Search.cpp | 10 +- src/slic3r/GUI/Tab.cpp | 5 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 271 ++++++++++++++++++++++++ src/slic3r/GUI/UnsavedChangesDialog.hpp | 151 +++++++++++++ 6 files changed, 437 insertions(+), 8 deletions(-) create mode 100644 src/slic3r/GUI/UnsavedChangesDialog.cpp create mode 100644 src/slic3r/GUI/UnsavedChangesDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 20ea4e33a..cd28d6eb2 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -163,6 +163,8 @@ set(SLIC3R_GUI_SOURCES GUI/InstanceCheck.hpp GUI/Search.cpp GUI/Search.hpp + GUI/UnsavedChangesDialog.cpp + GUI/UnsavedChangesDialog.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index db551610b..6fa942915 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -86,6 +86,12 @@ class ConfigWizard; static wxString dots("…", wxConvUTF8); +// Does our wxWidgets version support markup? +// https://github.com/prusa3d/PrusaSlicer/issues/4282#issuecomment-634676371 +#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) + #define SUPPORTS_MARKUP +#endif + class GUI_App : public wxApp { bool m_initialized { false }; diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 2a2af5336..6eab6b72a 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -28,12 +28,6 @@ using GUI::into_u8; namespace Search { -// Does our wxWidgets version support markup? -// https://github.com/prusa3d/PrusaSlicer/issues/4282#issuecomment-634676371 -#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) - #define SEARCH_SUPPORTS_MARKUP -#endif - static char marker_by_type(Preset::Type type, PrinterTechnology pt) { switch(type) { @@ -264,7 +258,7 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) std::string label_u8 = into_u8(label); std::string label_plain = label_u8; -#ifdef SEARCH_SUPPORTS_MARKUP +#ifdef SUPPORTS_MARKUP boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart)), ""); boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd)), ""); #else @@ -442,7 +436,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher) wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); -#ifdef SEARCH_SUPPORTS_MARKUP +#ifdef SUPPORTS_MARKUP markupRenderer->EnableMarkup(); #endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9d37362ba..49317f802 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -36,6 +36,7 @@ #include "MainFrame.hpp" #include "format.hpp" #include "PhysicalPrinterDialog.hpp" +#include "UnsavedChangesDialog.hpp" namespace Slic3r { namespace GUI { @@ -3131,6 +3132,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { + UnsavedChangesDialog dlg(m_type); + dlg.ShowModal(); + + if (presets == nullptr) presets = m_presets; // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp new file mode 100644 index 000000000..21da295d4 --- /dev/null +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -0,0 +1,271 @@ +#include "UnsavedChangesDialog.hpp" + +#include +#include +#include +#include +#include + +#include "wx/dataview.h" + +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/PresetBundle.hpp" +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "Tab.hpp" + +#define FTS_FUZZY_MATCH_IMPLEMENTATION +#include "fts_fuzzy_match.h" + +#include "imgui/imconfig.h" + +using boost::optional; + +namespace Slic3r { + +namespace GUI { + +// ---------------------------------------------------------------------------- +// ModelNode: a node inside UnsavedChangesModel +// ---------------------------------------------------------------------------- + +// preset(root) node +ModelNode::ModelNode(const wxString& text, Preset::Type preset_type) : + m_parent(nullptr), + m_preset_type(preset_type), + m_text(text) +{ +} + +// group node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) : + m_parent(parent), + m_text(text) +{ +} + +// group node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, bool is_option) : + m_parent(parent), + m_text(text), + m_container(!is_option) +{ +} + + +// ---------------------------------------------------------------------------- +// UnsavedChangesModel +// ---------------------------------------------------------------------------- + +UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) +{ + int icon_id = 0; + for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" }) + m_icon[icon_id++] = ScalableBitmap(parent, icon); + + m_root = new ModelNode("Preset", Preset::TYPE_INVALID); +} + +UnsavedChangesModel::~UnsavedChangesModel() +{ + delete m_root; +} + +void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const +{ + wxASSERT(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + switch (col) + { + case colToggle: + variant = node->m_toggle; + break; + case colTypeIcon: + variant << node->m_type_icon; + break; + case colGroupIcon: + variant << node->m_group_icon; + break; + case colMarkedText: + variant =node->m_text; + break; + case colOldValue: + variant =node->m_text; + break; + case colNewValue: + variant =node->m_text; + break; + + default: + wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col); + } +} + +bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) +{ + assert(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + switch (col) + { + case colToggle: + node->m_toggle = variant.GetBool(); + return true; + case colTypeIcon: + node->m_type_icon << variant; + return true; + case colGroupIcon: + node->m_group_icon << variant; + return true; + case colMarkedText: + node->m_text = variant.GetString(); + return true; + case colOldValue: + node->m_text = variant.GetString(); + return true; + case colNewValue: + node->m_text = variant.GetString(); + return true; + default: + wxLogError("UnsavedChangesModel::SetValue: wrong column"); + } + return false; +} + +bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const +{ + assert(item.IsOk()); + + ModelNode* node = (ModelNode*)item.GetID(); + + // disable unchecked nodes + return !node->IsToggle(); +} + +wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(nullptr); + + ModelNode* node = (ModelNode*)item.GetID(); + + // "MyMusic" also has no parent + if (node == m_root) + return wxDataViewItem(nullptr); + + return wxDataViewItem((void*)node->GetParent()); +} + +bool UnsavedChangesModel::IsContainer(const wxDataViewItem& item) const +{ + // the invisble root node can have children + if (!item.IsOk()) + return true; + + ModelNode* node = (ModelNode*)item.GetID(); + return node->IsContainer(); +} + +unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const +{ + ModelNode* node = (ModelNode*)parent.GetID(); + if (!node) { + array.Add(wxDataViewItem((void*)m_root)); + return 1; + } + + if (node->GetChildCount() == 0) + return 0; + + unsigned int count = node->GetChildren().GetCount(); + for (unsigned int pos = 0; pos < count; pos++) { + ModelNode* child = node->GetChildren().Item(pos); + array.Add(wxDataViewItem((void*)child)); + } + + return count; +} + + +wxString UnsavedChangesModel::GetColumnType(unsigned int col) const +{ + if (col == colToggle) + return "bool"; + + if (col < colMarkedText) + return "wxBitmap"; + + return "string"; +} + + +//------------------------------------------ +// UnsavedChangesDialog +//------------------------------------------ + +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) + : DPIDialog(NULL, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SetBackgroundColour(bgr_clr); + + int border = 10; + int em = em_unit(); + + changes_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 60), wxBORDER_SIMPLE); + changes_tree_model = new UnsavedChangesModel(this); + changes_tree->AssociateModel(changes_tree_model); + + changes_tree->AppendToggleColumn(L"\u2610", UnsavedChangesModel::colToggle);//2610,11,12 //2714 + changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colTypeIcon); + changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colGroupIcon); + + wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); + +#ifdef SUPPORTS_MARKUP + markupRenderer->EnableMarkup(); +#endif + + changes_tree->AppendColumn(new wxDataViewColumn("", markupRenderer, UnsavedChangesModel::colMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + changes_tree->AppendColumn(new wxDataViewColumn("Old value", markupRenderer, UnsavedChangesModel::colOldValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + changes_tree->AppendColumn(new wxDataViewColumn("New value", markupRenderer, UnsavedChangesModel::colNewValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + + wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for the current preset") + ":"), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(changes_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(cancel_btn, 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + +void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_CANCEL }); + + const wxSize& size = wxSize(80 * em, 60 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void UnsavedChangesDialog::on_sys_color_changed() +{ + // msw_rescale updates just icons, so use it +// changes_tree_model->msw_rescale(); + + Refresh(); +} + + +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp new file mode 100644 index 000000000..a3ee7d984 --- /dev/null +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -0,0 +1,151 @@ +#ifndef slic3r_UnsavedChangesDialog_hpp_ +#define slic3r_UnsavedChangesDialog_hpp_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "libslic3r/Preset.hpp" + +namespace Slic3r { + +namespace GUI{ + +// ---------------------------------------------------------------------------- +// ModelNode: a node inside UnsavedChangesModel +// ---------------------------------------------------------------------------- + +class ModelNode; +WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); + +class ModelNode +{ + ModelNode* m_parent; + ModelNodePtrArray m_children; + wxBitmap m_empty_bmp; + Preset::Type m_preset_type {Preset::TYPE_INVALID}; + + // TODO/FIXME: + // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) + // needs to know in advance if a node is or _will be_ a container. + // Thus implementing: + // bool IsContainer() const + // { return m_children.GetCount()>0; } + // doesn't work with wxGTK when UnsavedChangesModel::AddToClassical is called + // AND the classical node was removed (a new node temporary without children + // would be added to the control) + bool m_container {true}; + +public: + + bool m_toggle {true}; + wxBitmap m_type_icon; + wxBitmap m_group_icon; + wxString m_text; + wxString m_old_value; + wxString m_new_value; + + // preset(root) node + ModelNode(const wxString& text, Preset::Type preset_type); + + // group node + ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); + + // group node + ModelNode(ModelNode* parent, const wxString& text, bool is_option); + + ~ModelNode() { + // free all our children nodes + size_t count = m_children.GetCount(); + for (size_t i = 0; i < count; i++) { + ModelNode* child = m_children[i]; + delete child; + } + } + + bool IsContainer() const { return m_container; } + bool IsToggle() const { return m_toggle; } + + ModelNode* GetParent() { return m_parent; } + ModelNodePtrArray& GetChildren() { return m_children; } + ModelNode* GetNthChild(unsigned int n) { return m_children.Item(n); } + unsigned int GetChildCount() const { return m_children.GetCount(); } + + void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } + void Append(ModelNode* child) { m_children.Add(child); } +}; + + +// ---------------------------------------------------------------------------- +// UnsavedChangesModel +// ---------------------------------------------------------------------------- + +class UnsavedChangesModel : public wxDataViewModel +{ + ModelNode* m_root; + ScalableBitmap m_icon[5]; + +public: + enum { + colToggle, + colTypeIcon, + colGroupIcon, + colMarkedText, + colOldValue, + colNewValue, + colMax + }; + + UnsavedChangesModel(wxWindow* parent); + ~UnsavedChangesModel(); + + virtual unsigned int GetColumnCount() const override { return colMax; } + virtual wxString GetColumnType(unsigned int col) const override; + + virtual wxDataViewItem GetParent(const wxDataViewItem& item) const override; + virtual unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + virtual void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + virtual bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; + virtual bool IsContainer(const wxDataViewItem& item) const override; + +}; + + +//------------------------------------------ +// UnsavedChangesDialog +//------------------------------------------ +class UnsavedChangesDialog : public DPIDialog +{ + wxDataViewCtrl* changes_tree{ nullptr }; + UnsavedChangesModel* changes_tree_model{ nullptr }; + +public: + UnsavedChangesDialog(Preset::Type type); + ~UnsavedChangesDialog() {} + + void ProcessSelection(wxDataViewItem selection); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override; +}; + + +} +} + +#endif //slic3r_UnsavedChangesDialog_hpp_ From 1674d2af291b1933fd28258e5415501079f23ce9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Aug 2020 19:25:04 +0200 Subject: [PATCH 02/25] UnsavedChangesDialog improvements: * support markup text and colored icons for cells + Extended BitmapTextRenderer for using of markup text --- src/slic3r/GUI/ObjectDataViewModel.cpp | 76 +++- src/slic3r/GUI/ObjectDataViewModel.hpp | 19 +- src/slic3r/GUI/Search.cpp | 8 + src/slic3r/GUI/Search.hpp | 10 +- src/slic3r/GUI/Tab.cpp | 12 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 533 ++++++++++++++++++++---- src/slic3r/GUI/UnsavedChangesDialog.hpp | 108 +++-- 7 files changed, 664 insertions(+), 102 deletions(-) diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 336475d2e..badaa7a04 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -9,6 +9,12 @@ #include #include +#include "wx/generic/private/markuptext.h" +#include "wx/generic/private/rowheightcache.h" +#include "wx/generic/private/widthcalc.h" +#if wxUSE_ACCESSIBILITY +#include "wx/private/markupparser.h" +#endif // wxUSE_ACCESSIBILITY namespace Slic3r { @@ -1575,9 +1581,44 @@ wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) } #endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +BitmapTextRenderer::~BitmapTextRenderer() +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + delete m_markupText; +#endif // SUPPORTS_MARKUP +} + +#ifdef SUPPORTS_MARKUP +void BitmapTextRenderer::EnableMarkup(bool enable) +{ + if (enable) + { + if (!m_markupText) + { + m_markupText = new wxItemMarkupText(wxString()); + } + } + else + { + if (m_markupText) + { + delete m_markupText; + m_markupText = nullptr; + } + } +} +#endif // SUPPORTS_MARKUP + bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + m_markupText->SetMarkup(m_value.GetText()); +#endif // SUPPORTS_MARKUP + return true; } @@ -1589,6 +1630,11 @@ bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY wxString BitmapTextRenderer::GetAccessibleDescription() const { +#ifdef SUPPORTS_MARKUP + if (m_markupText) + return wxMarkupParser::Strip(m_text); +#endif // SUPPORTS_MARKUP + return m_value.GetText(); } #endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -1609,7 +1655,17 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) xoffset = icon_sz.x + 4; } - RenderText(m_value.GetText(), xoffset, rect, dc, state); +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + int flags = 0; + + rect.x += xoffset; + m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + } + else +#endif // SUPPORTS_MARKUP + RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; } @@ -1618,7 +1674,23 @@ wxSize BitmapTextRenderer::GetSize() const { if (!m_value.GetText().empty()) { - wxSize size = GetTextExtent(m_value.GetText()); + wxSize size; +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + wxDataViewCtrl* const view = GetView(); + wxClientDC dc(view); + if (GetAttr().HasFont()) + dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); + + size = m_markupText->Measure(dc); + } + else +#endif // SUPPORTS_MARKUP + size = GetTextExtent(m_value.GetText()); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); if (m_value.GetBitmap().IsOk()) size.x += m_value.GetBitmap().GetWidth() + 4; diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c18484266..c8545e4a4 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -2,9 +2,10 @@ #define slic3r_GUI_ObjectDataViewModel_hpp_ #include - #include +#include "GUI_App.hpp" + namespace Slic3r { enum class ModelVolumeType : int; @@ -83,9 +84,19 @@ public: ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), m_parent(parent) - {} + { +#ifdef SUPPORTS_MARKUP + m_markupText = nullptr; +#endif // SUPPORTS_MARKUP + } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ~BitmapTextRenderer(); + +#ifdef SUPPORTS_MARKUP + void EnableMarkup(bool enable = true); +#endif // SUPPORTS_MARKUP + bool SetValue(const wxVariant& value); bool GetValue(wxVariant& value) const; #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY @@ -114,6 +125,10 @@ private: DataViewBitmapText m_value; bool m_was_unusable_symbol{ false }; wxWindow* m_parent{ nullptr }; + +#ifdef SUPPORTS_MARKUP + class wxItemMarkupText* m_markupText; +#endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 6eab6b72a..d13ef469f 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -321,6 +321,14 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const return options[found[pos_in_filter].option_idx]; } +const Option& OptionsSearcher::get_option(const std::string& opt_key) const +{ + auto it = std::upper_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); + assert(it != options.end()); + + return options[it - options.begin()]; +} + void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category) { groups_and_categories[opt_key] = GroupAndCategory{group, category}; diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 8202222e9..a57e0d015 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -37,8 +37,8 @@ struct GroupAndCategory { }; struct Option { - bool operator<(const Option& other) const { return other.label > this->label; } - bool operator>(const Option& other) const { return other.label < this->label; } +// bool operator<(const Option& other) const { return other.label > this->label; } + bool operator<(const Option& other) const { return other.opt_key > this->opt_key; } // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters, // though for some languages (Chinese?) it may not work correctly. @@ -116,12 +116,18 @@ public: const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; } const Option& get_option(size_t pos_in_filter) const; + const Option& get_option(const std::string& opt_key) const; const std::vector& found_options() { return found; } const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; } std::string& search_string() { return search_line; } void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; } + + void sort_options_by_opt_key() { + std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) { + return o1.opt_key < o2.opt_key; }); + } }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 49317f802..dbb8761d3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3133,8 +3133,16 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { UnsavedChangesDialog dlg(m_type); - dlg.ShowModal(); - + if (dlg.ShowModal() == wxID_CANCEL) + return false; + if (dlg.just_continue()) + return true; + if (dlg.save_preset()) + // save selected changes + return false; + if (dlg.move_preset()) + // move selected changes + return false; if (presets == nullptr) presets = m_presets; // Display a dialog showing the dirty options in a human readable form. diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 21da295d4..46f086765 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -13,11 +13,12 @@ #include "GUI_App.hpp" #include "Plater.hpp" #include "Tab.hpp" +#include "ObjectDataViewModel.hpp" #define FTS_FUZZY_MATCH_IMPLEMENTATION #include "fts_fuzzy_match.h" -#include "imgui/imconfig.h" +#include "BitmapCache.hpp" using boost::optional; @@ -29,12 +30,39 @@ namespace GUI { // ModelNode: a node inside UnsavedChangesModel // ---------------------------------------------------------------------------- +static const std::map type_icon_names = { + {Preset::TYPE_PRINT, "cog" }, + {Preset::TYPE_SLA_PRINT, "cog" }, + {Preset::TYPE_FILAMENT, "spool" }, + {Preset::TYPE_SLA_MATERIAL, "resin" }, + {Preset::TYPE_PRINTER, "sla_printer" }, +}; + +static std::string black = "#000000"; +static std::string grey = "#808080"; +static std::string orange = "#ed6b21"; + +static void color_string(wxString& str, const std::string& color) +{ +#ifdef SUPPORTS_MARKUP + str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); +#endif +} + +static void make_string_bold(wxString& str) +{ +#ifdef SUPPORTS_MARKUP + str = from_u8((boost::format("%1%") % into_u8(str)).str()); +#endif +} + // preset(root) node -ModelNode::ModelNode(const wxString& text, Preset::Type preset_type) : +ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : m_parent(nullptr), m_preset_type(preset_type), m_text(text) { + m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); } // group node @@ -42,38 +70,230 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& m_parent(parent), m_text(text) { + m_icon = create_scaled_bitmap(icon_name); } -// group node -ModelNode::ModelNode(ModelNode* parent, const wxString& text, bool is_option) : +// category node +ModelNode::ModelNode(ModelNode* parent, const wxString& text) : m_parent(parent), - m_text(text), - m_container(!is_option) + m_text(text) { } +wxBitmap ModelNode::get_bitmap(const wxString& color) +{ + /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. + * So set sizes for solid_colored icons used for filament preset + * and scale them in respect to em_unit value + */ + const double em = em_unit(m_parent_win); + const int icon_width = lround(6.4 * em); + const int icon_height = lround(1.6 * em); + + BitmapCache bmp_cache; + unsigned char rgb[3]; + BitmapCache::parse_color(into_u8(color), rgb); + // there is no need to scale created solid bitmap + return bmp_cache.mksolid(icon_width, icon_height, rgb, true); +} + +// option node +ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) : + m_parent(parent), + m_old_color(old_value.StartsWith("#") ? old_value : ""), + m_new_color(new_value.StartsWith("#") ? new_value : ""), + m_container(false), + m_text(text), + m_old_value(old_value), + m_new_value(new_value) +{ + // check if old/new_value is color + if (m_old_color.IsEmpty()) { + if (!m_new_color.IsEmpty()) + m_old_value = _L("Undef"); + } + else { + m_old_color_bmp = get_bitmap(m_old_color); + m_old_value.Clear(); + } + + if (m_new_color.IsEmpty()) { + if (!m_old_color.IsEmpty()) + m_new_value = _L("Undef"); + } + else { + m_new_color_bmp = get_bitmap(m_new_color); + m_new_value.Clear(); + } + + // "color" strings + color_string(m_old_value, black); + color_string(m_new_value, orange); +} + +void ModelNode::UpdateEnabling() +{ +#ifdef SUPPORTS_MARKUP + auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) + { + std::string old_val = into_u8(str); + boost::replace_all(old_val, clr_from, clr_to); + str = from_u8(old_val); + }; + + if (!m_toggle) { + change_text_color(m_text, black, grey); + change_text_color(m_old_value, black, grey); + change_text_color(m_new_value, orange,grey); + } + else { + change_text_color(m_text, grey, black); + change_text_color(m_old_value, grey, black); + change_text_color(m_new_value, grey, orange); + } +#endif + // update icons for the colors + if (!m_old_color.IsEmpty()) + m_old_color_bmp = get_bitmap(m_toggle? m_old_color : grey); + if (!m_new_color.IsEmpty()) + m_new_color_bmp = get_bitmap(m_toggle? m_new_color : grey); +} + // ---------------------------------------------------------------------------- // UnsavedChangesModel // ---------------------------------------------------------------------------- -UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) +UnsavedChangesModel::UnsavedChangesModel(wxWindow* parent) : + m_parent_win(parent) { - int icon_id = 0; - for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" }) - m_icon[icon_id++] = ScalableBitmap(parent, icon); - - m_root = new ModelNode("Preset", Preset::TYPE_INVALID); } UnsavedChangesModel::~UnsavedChangesModel() { - delete m_root; + for (ModelNode* preset_node : m_preset_nodes) + delete preset_node; +} + +wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset_name) +{ + // "color" strings + color_string(preset_name, black); + make_string_bold(preset_name); + + auto preset = new ModelNode(type, preset_name); + m_preset_nodes.emplace_back(preset); + + wxDataViewItem child((void*)preset); + wxDataViewItem parent(nullptr); + + ItemAdded(parent, child); + return child; +} + +ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* option = new ModelNode(group_node, option_name, old_value, new_value); + group_node->Append(option); + ItemAdded(wxDataViewItem((void*)group_node), wxDataViewItem((void*)option)); + + return option; +} + +ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxString group_name, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* group_node = new ModelNode(category_node, group_name); + category_node->Append(group_node); + wxDataViewItem group_item = wxDataViewItem((void*)group_node); + ItemAdded(wxDataViewItem((void*)category_node), group_item); + m_ctrl->Expand(group_item); + + return AddOption(group_node, option_name, old_value, new_value); +} + +ModelNode* UnsavedChangesModel::AddOptionWithGroupAndCategory(ModelNode* preset_node, wxString category_name, wxString group_name, wxString option_name, wxString old_value, wxString new_value) +{ + ModelNode* category_node = new ModelNode(preset_node, category_name, "cog"); + preset_node->Append(category_node); + ItemAdded(wxDataViewItem((void*)preset_node), wxDataViewItem((void*)category_node)); + + return AddOptionWithGroup(category_node, group_name, option_name, old_value, new_value); +} + +wxDataViewItem UnsavedChangesModel::AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, + wxString old_value, wxString new_value) +{ + // "color" strings + color_string(category_name, black); + color_string(group_name, black); + color_string(option_name, black); + + // "make" strings bold + make_string_bold(category_name); + make_string_bold(group_name); + + // add items + for (ModelNode* preset : m_preset_nodes) + if (preset->type() == type) + { + for (ModelNode* category : preset->GetChildren()) + if (category->text() == category_name) + { + for (ModelNode* group : category->GetChildren()) + if (group->text() == group_name) + return wxDataViewItem((void*)AddOption(group, option_name, old_value, new_value)); + + return wxDataViewItem((void*)AddOptionWithGroup(category, group_name, option_name, old_value, new_value)); + } + + return wxDataViewItem((void*)AddOptionWithGroupAndCategory(preset, category_name, group_name, option_name, old_value, new_value)); + } + + return wxDataViewItem(nullptr); +} + +static void update_children(ModelNode* parent) +{ + if (parent->IsContainer()) { + bool toggle = parent->IsToggled(); + for (ModelNode* child : parent->GetChildren()) { + child->Toggle(toggle); + child->UpdateEnabling(); + update_children(child); + } + } +} + +static void update_parents(ModelNode* node) +{ + ModelNode* parent = node->GetParent(); + if (parent) { + bool toggle = false; + for (ModelNode* child : parent->GetChildren()) { + if (child->IsToggled()) { + toggle = true; + break; + } + } + parent->Toggle(toggle); + parent->UpdateEnabling(); + update_parents(parent); + } +} + +void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item) +{ + assert(item.IsOk()); + ModelNode* node = (ModelNode*)item.GetID(); + node->UpdateEnabling(); + + update_children(node); + update_parents(node); } void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const { - wxASSERT(item.IsOk()); + assert(item.IsOk()); ModelNode* node = (ModelNode*)item.GetID(); switch (col) @@ -81,20 +301,14 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colToggle: variant = node->m_toggle; break; - case colTypeIcon: - variant << node->m_type_icon; - break; - case colGroupIcon: - variant << node->m_group_icon; - break; - case colMarkedText: - variant =node->m_text; + case colIconText: + variant << DataViewBitmapText(node->m_text, node->m_icon); break; case colOldValue: - variant =node->m_text; + variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); break; case colNewValue: - variant =node->m_text; + variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp); break; default: @@ -109,24 +323,27 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte ModelNode* node = (ModelNode*)item.GetID(); switch (col) { + case colIconText: { + DataViewBitmapText data; + data << variant; + node->m_icon = data.GetBitmap(); + node->m_text = data.GetText(); + return true; } case colToggle: node->m_toggle = variant.GetBool(); return true; - case colTypeIcon: - node->m_type_icon << variant; - return true; - case colGroupIcon: - node->m_group_icon << variant; - return true; - case colMarkedText: - node->m_text = variant.GetString(); - return true; - case colOldValue: - node->m_text = variant.GetString(); - return true; - case colNewValue: - node->m_text = variant.GetString(); - return true; + case colOldValue: { + DataViewBitmapText data; + data << variant; + node->m_old_color_bmp = data.GetBitmap(); + node->m_old_value = data.GetText(); + return true; } + case colNewValue: { + DataViewBitmapText data; + data << variant; + node->m_new_color_bmp = data.GetBitmap(); + node->m_new_value = data.GetText(); + return true; } default: wxLogError("UnsavedChangesModel::SetValue: wrong column"); } @@ -136,11 +353,11 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte bool UnsavedChangesModel::IsEnabled(const wxDataViewItem& item, unsigned int col) const { assert(item.IsOk()); - - ModelNode* node = (ModelNode*)item.GetID(); + if (col == colToggle) + return true; // disable unchecked nodes - return !node->IsToggle(); + return ((ModelNode*)item.GetID())->IsToggled(); } wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const @@ -152,7 +369,7 @@ wxDataViewItem UnsavedChangesModel::GetParent(const wxDataViewItem& item) const ModelNode* node = (ModelNode*)item.GetID(); // "MyMusic" also has no parent - if (node == m_root) + if (node->IsRoot()) return wxDataViewItem(nullptr); return wxDataViewItem((void*)node->GetParent()); @@ -172,8 +389,9 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa { ModelNode* node = (ModelNode*)parent.GetID(); if (!node) { - array.Add(wxDataViewItem((void*)m_root)); - return 1; + for (auto preset_node : m_preset_nodes) + array.Add(wxDataViewItem((void*)preset_node)); + return m_preset_nodes.size(); } if (node->GetChildCount() == 0) @@ -191,13 +409,16 @@ unsigned int UnsavedChangesModel::GetChildren(const wxDataViewItem& parent, wxDa wxString UnsavedChangesModel::GetColumnType(unsigned int col) const { - if (col == colToggle) + switch (col) + { + case colToggle: return "bool"; - - if (col < colMarkedText) - return "wxBitmap"; - - return "string"; + case colIconText: + case colOldValue: + case colNewValue: + default: + return "DataViewBitmapText";//"string"; + } } @@ -214,43 +435,215 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) int border = 10; int em = em_unit(); - changes_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 60), wxBORDER_SIMPLE); - changes_tree_model = new UnsavedChangesModel(this); - changes_tree->AssociateModel(changes_tree_model); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT); + m_tree_model = new UnsavedChangesModel(this); + m_tree->AssociateModel(m_tree_model); + m_tree_model->SetAssociatedControl(m_tree); - changes_tree->AppendToggleColumn(L"\u2610", UnsavedChangesModel::colToggle);//2610,11,12 //2714 - changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colTypeIcon); - changes_tree->AppendBitmapColumn("", UnsavedChangesModel::colGroupIcon); - - wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer(); + m_tree->AppendToggleColumn(/*L"\u2714"*/"", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em, wxALIGN_NOT);//2610,11,12 //2714 + BitmapTextRenderer* renderer = new BitmapTextRenderer(m_tree); #ifdef SUPPORTS_MARKUP - markupRenderer->EnableMarkup(); + renderer->EnableMarkup(); #endif + m_tree->AppendColumn(new wxDataViewColumn("", renderer, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); + m_tree->AppendColumn(new wxDataViewColumn("Old value", renderer, UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(new wxDataViewColumn("New value", renderer, UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); - changes_tree->AppendColumn(new wxDataViewColumn("", markupRenderer, UnsavedChangesModel::colMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); - changes_tree->AppendColumn(new wxDataViewColumn("Old value", markupRenderer, UnsavedChangesModel::colOldValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); - changes_tree->AppendColumn(new wxDataViewColumn("New value", markupRenderer, UnsavedChangesModel::colNewValue, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT)); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); - wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL); + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + + Tab* tab = wxGetApp().get_tab(type); + assert(tab); + + PresetCollection* presets = tab->get_presets(); + + wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); + auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); + save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + buttons->Insert(0, save_btn, 0, wxLEFT, 5); + + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); + auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); + move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + buttons->Insert(1, move_btn, 0, wxLEFT, 5); + + auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); + continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + buttons->Insert(2, continue_btn, 0, wxLEFT, 5); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for the current preset") + ":"), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(changes_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(cancel_btn, 0, wxEXPAND | wxALL, border); + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + + update(type); SetSizer(topSizer); topSizer->SetSizeHints(this); } +void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) +{ + if (event.GetColumn() != UnsavedChangesModel::colToggle) + return; + + wxDataViewItem item = event.GetItem(); + + m_tree_model->UpdateItemEnabling(item); + m_tree->Refresh(); +} + +void UnsavedChangesDialog::close(Action action) +{ + m_action = action; + this->EndModal(wxID_CLOSE); +} + +template +wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config) +{ + const std::vector& names = config.def()->options.at(opt_key).enum_labels;//ConfigOptionEnum::get_enum_names(); + T val = config.option>(opt_key)->value; + return from_u8(_u8L(names[static_cast(val)])); +} + +static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) +{ + wxString out; + + // FIXME controll, if opt_key has index + int opt_idx = 0; + + ConfigOptionType type = config.def()->options.at(opt_key).type; + + switch (type) { + case coInt: + return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str()); + case coInts: { + const ConfigOptionInts* opt = config.opt(opt_key); + if (opt) + return from_u8((boost::format("%1%") % opt->get_at(opt_idx)).str()); + break; + } + case coBool: + return config.opt_bool(opt_key) ? "true" : "false"; + case coBools: { + const ConfigOptionBools* opt = config.opt(opt_key); + if (opt) + return opt->get_at(opt_idx) ? "true" : "false"; + break; + } + case coPercent: + return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); + case coPercents: { + const ConfigOptionPercents* opt = config.opt(opt_key); + if (opt) + return from_u8((boost::format("%1%%%") % int(opt->get_at(opt_idx))).str()); + break; + } + case coFloat: + return double_to_string(config.opt_float(opt_key)); + case coFloats: { + const ConfigOptionFloats* opt = config.opt(opt_key); + if (opt) + return double_to_string(opt->get_at(opt_idx)); + break; + } + case coString: + return from_u8(config.opt_string(opt_key)); + case coStrings: { + const ConfigOptionStrings* strings = config.opt(opt_key); + if (strings) { + if (opt_key == "compatible_printers" || opt_key == "compatible_prints") { + if (strings->empty()) + return _L("All"); + for (size_t id = 0; id < strings->size(); id++) + out += from_u8(strings->get_at(id)) + "\n"; + out.RemoveLast(1); + return out; + } + if (!strings->empty()) + return from_u8(strings->get_at(opt_idx)); + } + break; + } + case coFloatOrPercent: { + const ConfigOptionFloatOrPercent* opt = config.opt(opt_key); + if (opt) + out = double_to_string(opt->value) + (opt->percent ? "%" : ""); + return out; + } + case coEnum: { + if (opt_key == "top_fill_pattern" || + opt_key == "bottom_fill_pattern" || + opt_key == "fill_pattern") + return get_string_from_enum(opt_key, config); + if (opt_key == "gcode_flavor") + return get_string_from_enum(opt_key, config); + if (opt_key == "ironing_type") + return get_string_from_enum(opt_key, config); + if (opt_key == "support_material_pattern") + return get_string_from_enum(opt_key, config); + if (opt_key == "seam_position") + return get_string_from_enum(opt_key, config); + if (opt_key == "display_orientation") + return get_string_from_enum(opt_key, config); + if (opt_key == "support_pillar_connection_mode") + return get_string_from_enum(opt_key, config); + break; + } + case coPoints: { + /* + if (opt_key == "bed_shape") { + config.option(opt_key)->values = boost::any_cast>(value); + break; + } + ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; + config.option(opt_key)->set_at(vec_new, opt_index, 0); + */ + return "Points"; + } + default: + break; + } + return out; +} + +void UnsavedChangesDialog::update(Preset::Type type) +{ + Tab* tab = wxGetApp().get_tab(type); + assert(tab); + + PresetCollection* presets = tab->get_presets(); + // Display a dialog showing the dirty options in a human readable form. + const DynamicPrintConfig& old_config = presets->get_selected_preset().config; + const DynamicPrintConfig& new_config = presets->get_edited_preset().config; + + m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + + Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); + searcher.sort_options_by_opt_key(); + + // Collect dirty options. + for (const std::string& opt_key : presets->current_dirty_options()) { + const Search::Option& option = searcher.get_option(opt_key); + + m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, + get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)); + } +} + + void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); - msw_buttons_rescale(this, em, { wxID_CANCEL }); + msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); - const wxSize& size = wxSize(80 * em, 60 * em); + const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); Fit(); @@ -260,7 +653,7 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) void UnsavedChangesDialog::on_sys_color_changed() { // msw_rescale updates just icons, so use it -// changes_tree_model->msw_rescale(); +// m_tree_model->msw_rescale(); Refresh(); } diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index a3ee7d984..c4a02d7bc 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -31,11 +31,17 @@ WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); class ModelNode { + wxWindow* m_parent_win{ nullptr }; + ModelNode* m_parent; ModelNodePtrArray m_children; wxBitmap m_empty_bmp; Preset::Type m_preset_type {Preset::TYPE_INVALID}; + // saved values for colors if they exist + wxString m_old_color; + wxString m_new_color; + // TODO/FIXME: // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) // needs to know in advance if a node is or _will be_ a container. @@ -47,23 +53,29 @@ class ModelNode // would be added to the control) bool m_container {true}; + wxBitmap get_bitmap(const wxString& color); + public: bool m_toggle {true}; - wxBitmap m_type_icon; - wxBitmap m_group_icon; + wxBitmap m_icon; + wxBitmap m_old_color_bmp; + wxBitmap m_new_color_bmp; wxString m_text; wxString m_old_value; wxString m_new_value; // preset(root) node - ModelNode(const wxString& text, Preset::Type preset_type); + ModelNode(Preset::Type preset_type, const wxString& text); - // group node + // category node ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); // group node - ModelNode(ModelNode* parent, const wxString& text, bool is_option); + ModelNode(ModelNode* parent, const wxString& text); + + // option node + ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value); ~ModelNode() { // free all our children nodes @@ -75,15 +87,21 @@ public: } bool IsContainer() const { return m_container; } - bool IsToggle() const { return m_toggle; } + bool IsToggled() const { return m_toggle; } + void Toggle(bool toggle = true) { m_toggle = toggle; } + bool IsRoot() const { return m_parent == nullptr; } + Preset::Type type() const { return m_preset_type; } + const wxString& text() const { return m_text; } ModelNode* GetParent() { return m_parent; } ModelNodePtrArray& GetChildren() { return m_children; } ModelNode* GetNthChild(unsigned int n) { return m_children.Item(n); } unsigned int GetChildCount() const { return m_children.GetCount(); } - void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } - void Append(ModelNode* child) { m_children.Add(child); } + void Insert(ModelNode* child, unsigned int n) { m_children.Insert(child, n); } + void Append(ModelNode* child) { m_children.Add(child); } + + void UpdateEnabling(); }; @@ -93,15 +111,31 @@ public: class UnsavedChangesModel : public wxDataViewModel { - ModelNode* m_root; - ScalableBitmap m_icon[5]; + wxWindow* m_parent_win {nullptr}; + std::vector m_preset_nodes; + + wxDataViewCtrl* m_ctrl{ nullptr }; + + ModelNode *AddOption(ModelNode *group_node, + wxString option_name, + wxString old_value, + wxString new_value); + ModelNode *AddOptionWithGroup(ModelNode *category_node, + wxString group_name, + wxString option_name, + wxString old_value, + wxString new_value); + ModelNode *AddOptionWithGroupAndCategory(ModelNode *preset_node, + wxString category_name, + wxString group_name, + wxString option_name, + wxString old_value, + wxString new_value); public: enum { colToggle, - colTypeIcon, - colGroupIcon, - colMarkedText, + colIconText, colOldValue, colNewValue, colMax @@ -110,18 +144,28 @@ public: UnsavedChangesModel(wxWindow* parent); ~UnsavedChangesModel(); - virtual unsigned int GetColumnCount() const override { return colMax; } - virtual wxString GetColumnType(unsigned int col) const override; + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } - virtual wxDataViewItem GetParent(const wxDataViewItem& item) const override; - virtual unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + wxDataViewItem AddPreset(Preset::Type type, wxString preset_name); + wxDataViewItem AddOption(Preset::Type type, wxString category_name, wxString group_name, wxString option_name, + wxString old_value, wxString new_value); - virtual void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; - virtual bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + void UpdateItemEnabling(wxDataViewItem item); - virtual bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; - virtual bool IsContainer(const wxDataViewItem& item) const override; + unsigned int GetColumnCount() const override { return colMax; } + wxString GetColumnType(unsigned int col) const override; + wxDataViewItem GetParent(const wxDataViewItem& item) const override; + unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + bool IsEnabled(const wxDataViewItem& item, unsigned int col) const override; + bool IsContainer(const wxDataViewItem& item) const override; + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } }; @@ -130,14 +174,30 @@ public: //------------------------------------------ class UnsavedChangesDialog : public DPIDialog { - wxDataViewCtrl* changes_tree{ nullptr }; - UnsavedChangesModel* changes_tree_model{ nullptr }; + wxDataViewCtrl* m_tree { nullptr }; + UnsavedChangesModel* m_tree_model { nullptr }; + + int m_save_btn_id { wxID_ANY }; + int m_move_btn_id { wxID_ANY }; + int m_continue_btn_id { wxID_ANY }; + + enum class Action { + Save, + Move, + Continue + } m_action; public: UnsavedChangesDialog(Preset::Type type); ~UnsavedChangesDialog() {} - void ProcessSelection(wxDataViewItem selection); + void update(Preset::Type type); + void item_value_changed(wxDataViewEvent &event); + void close(Action action); + + bool save_preset() const { return m_action == Action::Save; } + bool move_preset() const { return m_action == Action::Move; } + bool just_continue() const { return m_action == Action::Continue; } protected: void on_dpi_changed(const wxRect& suggested_rect) override; From 93c1671e09b65905ac7da7bba60dcea15d9f5e4e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 5 Aug 2020 20:26:40 +0200 Subject: [PATCH 03/25] Custom renderers extracted from the ObjectDataViewModel --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/ExtraRenderers.cpp | 314 +++++++++++++++++++++++++ src/slic3r/GUI/ExtraRenderers.hpp | 162 +++++++++++++ src/slic3r/GUI/GUI_ObjectList.cpp | 14 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 305 ------------------------ src/slic3r/GUI/ObjectDataViewModel.hpp | 153 +----------- 6 files changed, 490 insertions(+), 460 deletions(-) create mode 100644 src/slic3r/GUI/ExtraRenderers.cpp create mode 100644 src/slic3r/GUI/ExtraRenderers.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index cd28d6eb2..b22a3cb1f 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -165,6 +165,8 @@ set(SLIC3R_GUI_SOURCES GUI/Search.hpp GUI/UnsavedChangesDialog.cpp GUI/UnsavedChangesDialog.hpp + GUI/ExtraRenderers.cpp + GUI/ExtraRenderers.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp new file mode 100644 index 000000000..494bfee6a --- /dev/null +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -0,0 +1,314 @@ +#include "ExtraRenderers.hpp" +#include "wxExtensions.hpp" +#include "GUI.hpp" +#include "I18N.hpp" + +#include +#include "wx/generic/private/markuptext.h" +#include "wx/generic/private/rowheightcache.h" +#include "wx/generic/private/widthcalc.h" +#if wxUSE_ACCESSIBILITY +#include "wx/private/markupparser.h" +#endif // wxUSE_ACCESSIBILITY + +using Slic3r::GUI::from_u8; +using Slic3r::GUI::into_u8; + + +//----------------------------------------------------------------------------- +// DataViewBitmapText +//----------------------------------------------------------------------------- + +wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) + +IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) + +// --------------------------------------------------------- +// BitmapTextRenderer +// --------------------------------------------------------- + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, + int align /*= wxDVR_DEFAULT_ALIGNMENT*/): +wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) +{ + SetMode(mode); + SetAlignment(align); +} +#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +BitmapTextRenderer::~BitmapTextRenderer() +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + delete m_markupText; +#endif // SUPPORTS_MARKUP +} + +#ifdef SUPPORTS_MARKUP +void BitmapTextRenderer::EnableMarkup(bool enable) +{ + if (enable) + { + if (!m_markupText) + { + m_markupText = new wxItemMarkupText(wxString()); + } + } + else + { + if (m_markupText) + { + delete m_markupText; + m_markupText = nullptr; + } + } +} +#endif // SUPPORTS_MARKUP + +bool BitmapTextRenderer::SetValue(const wxVariant &value) +{ + m_value << value; + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + m_markupText->SetMarkup(m_value.GetText()); +#endif // SUPPORTS_MARKUP + + return true; +} + +bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const +{ + return false; +} + +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY +wxString BitmapTextRenderer::GetAccessibleDescription() const +{ +#ifdef SUPPORTS_MARKUP + if (m_markupText) + return wxMarkupParser::Strip(m_text); +#endif // SUPPORTS_MARKUP + + return m_value.GetText(); +} +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + +bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { +#ifdef __APPLE__ + wxSize icon_sz = icon.GetScaledSize(); +#else + wxSize icon_sz = icon.GetSize(); +#endif + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); + xoffset = icon_sz.x + 4; + } + +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + int flags = 0; + + rect.x += xoffset; + m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + } + else +#endif // SUPPORTS_MARKUP + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapTextRenderer::GetSize() const +{ + if (!m_value.GetText().empty()) + { + wxSize size; +#ifdef SUPPORTS_MARKUP + if (m_markupText) + { + wxDataViewCtrl* const view = GetView(); + wxClientDC dc(view); + if (GetAttr().HasFont()) + dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); + + size = m_markupText->Measure(dc); + } + else +#endif // SUPPORTS_MARKUP + size = GetTextExtent(m_value.GetText()); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); + + if (m_value.GetBitmap().IsOk()) + size.x += m_value.GetBitmap().GetWidth() + 4; + return size; + } + return wxSize(80, 20); +} + + +wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + if (!can_create_editor_ctrl()) + return nullptr; + + DataViewBitmapText data; + data << value; + + m_was_unusable_symbol = false; + + wxPoint position = labelRect.GetPosition(); + if (data.GetBitmap().IsOk()) { + const int bmp_width = data.GetBitmap().GetWidth(); + position.x += bmp_width; + labelRect.SetWidth(labelRect.GetWidth() - bmp_width); + } + + wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), + position, labelRect.GetSize(), wxTE_PROCESS_ENTER); + text_editor->SetInsertionPointEnd(); + text_editor->SelectAll(); + + return text_editor; +} + +bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); + if (!text_editor || text_editor->GetValue().IsEmpty()) + return false; + + std::string chosen_name = into_u8(text_editor->GetValue()); + const char* unusable_symbols = "<>:/\\|?*\""; + for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { + if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { + m_was_unusable_symbol = true; + return false; + } + } + + // The icon can't be edited so get its old value and reuse it. + wxVariant valueOld; + GetView()->GetModel()->GetValue(valueOld, m_item, /*colName*/0); + + DataViewBitmapText bmpText; + bmpText << valueOld; + + // But replace the text with the value entered by user. + bmpText.SetText(text_editor->GetValue()); + + value << bmpText; + return true; +} + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +bool BitmapChoiceRenderer::SetValue(const wxVariant& value) +{ + m_value << value; + return true; +} + +bool BitmapChoiceRenderer::GetValue(wxVariant& value) const +{ + value << m_value; + return true; +} + +bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) +{ + int xoffset = 0; + + const wxBitmap& icon = m_value.GetBitmap(); + if (icon.IsOk()) + { + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + xoffset = icon.GetWidth() + 4; + + if (rect.height==0) + rect.height= icon.GetHeight(); + } + + RenderText(m_value.GetText(), xoffset, rect, dc, state); + + return true; +} + +wxSize BitmapChoiceRenderer::GetSize() const +{ + wxSize sz = GetTextExtent(m_value.GetText()); + + if (m_value.GetBitmap().IsOk()) + sz.x += m_value.GetBitmap().GetWidth() + 4; + + return sz; +} + + +wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) +{ + if (!can_create_editor_ctrl()) + return nullptr; + + std::vector icons = get_extruder_color_icons(); + if (icons.empty()) + return nullptr; + + DataViewBitmapText data; + data << value; + + auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, + labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), + 0, nullptr , wxCB_READONLY); + + int i=0; + for (wxBitmap* bmp : icons) { + if (i==0) { + c_editor->Append(_L("default"), *bmp); + ++i; + } + + c_editor->Append(wxString::Format("%d", i), *bmp); + ++i; + } + c_editor->SetSelection(atoi(data.GetText().c_str())); + + // to avoid event propagation to other sidebar items + c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { + evt.StopPropagation(); + // FinishEditing grabs new selection and triggers config update. We better call + // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. + this->FinishEditing(); + }); + + return c_editor; +} + +bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) +{ + wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; + int selection = c->GetSelection(); + if (selection < 0) + return false; + + DataViewBitmapText bmpText; + + bmpText.SetText(c->GetString(selection)); + bmpText.SetBitmap(c->GetItemBitmap(selection)); + + value << bmpText; + return true; +} + + diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp new file mode 100644 index 000000000..96cf34945 --- /dev/null +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_GUI_ExtraRenderers_hpp_ +#define slic3r_GUI_ExtraRenderers_hpp_ + +#include + +#if wxUSE_MARKUP && wxCHECK_VERSION(3, 1, 1) + #define SUPPORTS_MARKUP +#endif + +// ---------------------------------------------------------------------------- +// DataViewBitmapText: helper class used by BitmapTextRenderer +// ---------------------------------------------------------------------------- + +class DataViewBitmapText : public wxObject +{ +public: + DataViewBitmapText( const wxString &text = wxEmptyString, + const wxBitmap& bmp = wxNullBitmap) : + m_text(text), + m_bmp(bmp) + { } + + DataViewBitmapText(const DataViewBitmapText &other) + : wxObject(), + m_text(other.m_text), + m_bmp(other.m_bmp) + { } + + void SetText(const wxString &text) { m_text = text; } + wxString GetText() const { return m_text; } + void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } + const wxBitmap &GetBitmap() const { return m_bmp; } + + bool IsSameAs(const DataViewBitmapText& other) const { + return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); + } + + bool operator==(const DataViewBitmapText& other) const { + return IsSameAs(other); + } + + bool operator!=(const DataViewBitmapText& other) const { + return !IsSameAs(other); + } + +private: + wxString m_text; + wxBitmap m_bmp; + + wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); +}; +DECLARE_VARIANT_OBJECT(DataViewBitmapText) + +// ---------------------------------------------------------------------------- +// BitmapTextRenderer +// ---------------------------------------------------------------------------- +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +class BitmapTextRenderer : public wxDataViewRenderer +#else +class BitmapTextRenderer : public wxDataViewCustomRenderer +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING +{ +public: + BitmapTextRenderer(wxWindow* parent, + wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + + , int align = wxDVR_DEFAULT_ALIGNMENT +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + ); +#else + ) : + wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), + m_parent(parent) + { +#ifdef SUPPORTS_MARKUP + m_markupText = nullptr; +#endif // SUPPORTS_MARKUP + } +#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + ~BitmapTextRenderer(); + +#ifdef SUPPORTS_MARKUP + void EnableMarkup(bool enable = true); +#endif // SUPPORTS_MARKUP + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; +#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY + virtual wxString GetAccessibleDescription() const override; +#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override + { +#ifdef __WXOSX__ + return false; +#else + return true; +#endif + } + wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override; + bool WasCanceled() const { return m_was_unusable_symbol; } + + void set_can_create_editor_ctrl_function(std::function can_create_fn) { can_create_editor_ctrl = can_create_fn; } + +private: + DataViewBitmapText m_value; + bool m_was_unusable_symbol{ false }; + wxWindow* m_parent{ nullptr }; + + std::function can_create_editor_ctrl { nullptr }; + +#ifdef SUPPORTS_MARKUP + class wxItemMarkupText* m_markupText; +#endif // SUPPORTS_MARKUP +}; + + +// ---------------------------------------------------------------------------- +// BitmapChoiceRenderer +// ---------------------------------------------------------------------------- + +class BitmapChoiceRenderer : public wxDataViewCustomRenderer +{ +public: + BitmapChoiceRenderer(wxDataViewCellMode mode = +#ifdef __WXOSX__ + wxDATAVIEW_CELL_INERT +#else + wxDATAVIEW_CELL_EDITABLE +#endif + , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL + ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} + + bool SetValue(const wxVariant& value); + bool GetValue(wxVariant& value) const; + + virtual bool Render(wxRect cell, wxDC* dc, int state) override; + virtual wxSize GetSize() const override; + + bool HasEditorCtrl() const override { return true; } + wxWindow* CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) override; + bool GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override; + + void set_can_create_editor_ctrl_function(std::function can_create_fn) { can_create_editor_ctrl = can_create_fn; } + +private: + DataViewBitmapText m_value; + std::function can_create_editor_ctrl { nullptr }; +}; + + +#endif // slic3r_GUI_ExtraRenderers_hpp_ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 9d6b2b9cb..a434e39fd 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -277,7 +277,11 @@ void ObjectList::create_objects_ctrl() // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps - AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(this), + BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(this); + bmp_text_renderer->set_can_create_editor_ctrl_function([this]() { + return m_objects_model->GetItemType(GetSelection()) & (itVolume | itObject); + }); + AppendColumn(new wxDataViewColumn(_L("Name"), bmp_text_renderer, colName, 20*em, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); // column PrintableProperty (Icon) of the view control: @@ -285,11 +289,15 @@ void ObjectList::create_objects_ctrl() wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // column Extruder of the view control: - AppendColumn(new wxDataViewColumn(_(L("Extruder")), new BitmapChoiceRenderer(), + BitmapChoiceRenderer* bmp_choice_renderer = new BitmapChoiceRenderer(); + bmp_choice_renderer->set_can_create_editor_ctrl_function([this]() { + return m_objects_model->GetItemType(GetSelection()) & (itVolume | itLayer | itObject); + }); + AppendColumn(new wxDataViewColumn(_L("Extruder"), bmp_choice_renderer, colExtruder, 8*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE)); // column ItemEditing of the view control: - AppendBitmapColumn(_(L("Editing")), colEditing, wxDATAVIEW_CELL_INERT, 3*em, + AppendBitmapColumn(_L("Editing"), colEditing, wxDATAVIEW_CELL_INERT, 3*em, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); // For some reason under OSX on 4K(5K) monitors in wxDataViewColumn constructor doesn't set width of column. diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index badaa7a04..a42073dd0 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1555,311 +1555,6 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo DeleteWarningIcon(child); } } -/* -} -} -*/ -//----------------------------------------------------------------------------- -// DataViewBitmapText -//----------------------------------------------------------------------------- - -wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) - -IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) - -// --------------------------------------------------------- -// BitmapTextRenderer -// --------------------------------------------------------- - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -BitmapTextRenderer::BitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/, - int align /*= wxDVR_DEFAULT_ALIGNMENT*/): -wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) -{ - SetMode(mode); - SetAlignment(align); -} -#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -BitmapTextRenderer::~BitmapTextRenderer() -{ -#ifdef SUPPORTS_MARKUP - if (m_markupText) - delete m_markupText; -#endif // SUPPORTS_MARKUP -} - -#ifdef SUPPORTS_MARKUP -void BitmapTextRenderer::EnableMarkup(bool enable) -{ - if (enable) - { - if (!m_markupText) - { - m_markupText = new wxItemMarkupText(wxString()); - } - } - else - { - if (m_markupText) - { - delete m_markupText; - m_markupText = nullptr; - } - } -} -#endif // SUPPORTS_MARKUP - -bool BitmapTextRenderer::SetValue(const wxVariant &value) -{ - m_value << value; - -#ifdef SUPPORTS_MARKUP - if (m_markupText) - m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP - - return true; -} - -bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const -{ - return false; -} - -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY -wxString BitmapTextRenderer::GetAccessibleDescription() const -{ -#ifdef SUPPORTS_MARKUP - if (m_markupText) - return wxMarkupParser::Strip(m_text); -#endif // SUPPORTS_MARKUP - - return m_value.GetText(); -} -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - -bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { -#ifdef __APPLE__ - wxSize icon_sz = icon.GetScaledSize(); -#else - wxSize icon_sz = icon.GetSize(); -#endif - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); - xoffset = icon_sz.x + 4; - } - -#ifdef SUPPORTS_MARKUP - if (m_markupText) - { - int flags = 0; - - rect.x += xoffset; - m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); - } - else -#endif // SUPPORTS_MARKUP - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapTextRenderer::GetSize() const -{ - if (!m_value.GetText().empty()) - { - wxSize size; -#ifdef SUPPORTS_MARKUP - if (m_markupText) - { - wxDataViewCtrl* const view = GetView(); - wxClientDC dc(view); - if (GetAttr().HasFont()) - dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); - - size = m_markupText->Measure(dc); - } - else -#endif // SUPPORTS_MARKUP - size = GetTextExtent(m_value.GetText()); - - int lines = m_value.GetText().Freq('\n') + 1; - size.SetHeight(size.GetHeight() * lines); - - if (m_value.GetBitmap().IsOk()) - size.x += m_value.GetBitmap().GetWidth() + 4; - return size; - } - return wxSize(80, 20); -} - - -wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if ( !(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itObject)) ) - return nullptr; - - DataViewBitmapText data; - data << value; - - m_was_unusable_symbol = false; - - wxPoint position = labelRect.GetPosition(); - if (data.GetBitmap().IsOk()) { - const int bmp_width = data.GetBitmap().GetWidth(); - position.x += bmp_width; - labelRect.SetWidth(labelRect.GetWidth() - bmp_width); - } - - wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(), - position, labelRect.GetSize(), wxTE_PROCESS_ENTER); - text_editor->SetInsertionPointEnd(); - text_editor->SelectAll(); - - return text_editor; -} - -bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxTextCtrl* text_editor = wxDynamicCast(ctrl, wxTextCtrl); - if (!text_editor || text_editor->GetValue().IsEmpty()) - return false; - - std::string chosen_name = Slic3r::normalize_utf8_nfc(text_editor->GetValue().ToUTF8()); - const char* unusable_symbols = "<>:/\\|?*\""; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++) { - if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) { - m_was_unusable_symbol = true; - return false; - } - } - - // The icon can't be edited so get its old value and reuse it. - wxVariant valueOld; - GetView()->GetModel()->GetValue(valueOld, m_item, colName); - - DataViewBitmapText bmpText; - bmpText << valueOld; - - // But replace the text with the value entered by user. - bmpText.SetText(text_editor->GetValue()); - - value << bmpText; - return true; -} - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -bool BitmapChoiceRenderer::SetValue(const wxVariant& value) -{ - m_value << value; - return true; -} - -bool BitmapChoiceRenderer::GetValue(wxVariant& value) const -{ - value << m_value; - return true; -} - -bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; - - if (rect.height==0) - rect.height= icon.GetHeight(); - } - - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize BitmapChoiceRenderer::GetSize() const -{ - wxSize sz = GetTextExtent(m_value.GetText()); - - if (m_value.GetBitmap().IsOk()) - sz.x += m_value.GetBitmap().GetWidth() + 4; - - return sz; -} - - -wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) -{ - wxDataViewCtrl* const dv_ctrl = GetOwner()->GetOwner(); - ObjectDataViewModel* const model = dynamic_cast(dv_ctrl->GetModel()); - - if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itLayer | itObject))) - return nullptr; - - std::vector icons = get_extruder_color_icons(); - if (icons.empty()) - return nullptr; - - DataViewBitmapText data; - data << value; - - auto c_editor = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, - labelRect.GetTopLeft(), wxSize(labelRect.GetWidth(), -1), - 0, nullptr , wxCB_READONLY); - - int i=0; - for (wxBitmap* bmp : icons) { - if (i==0) { - c_editor->Append(_(L("default")), *bmp); - ++i; - } - - c_editor->Append(wxString::Format("%d", i), *bmp); - ++i; - } - c_editor->SetSelection(atoi(data.GetText().c_str())); - - // to avoid event propagation to other sidebar items - c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) { - evt.StopPropagation(); - // FinishEditing grabs new selection and triggers config update. We better call - // it explicitly, automatic update on KILL_FOCUS didn't work on Linux. - this->FinishEditing(); - }); - - return c_editor; -} - -bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) -{ - wxBitmapComboBox* c = (wxBitmapComboBox*)ctrl; - int selection = c->GetSelection(); - if (selection < 0) - return false; - - DataViewBitmapText bmpText; - - bmpText.SetText(c->GetString(selection)); - bmpText.SetBitmap(c->GetItemBitmap(selection)); - - value << bmpText; - return true; -} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c8545e4a4..12480139d 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -4,7 +4,7 @@ #include #include -#include "GUI_App.hpp" +#include "ExtraRenderers.hpp" namespace Slic3r { @@ -15,157 +15,6 @@ namespace GUI { typedef double coordf_t; typedef std::pair t_layer_height_range; -// ---------------------------------------------------------------------------- -// DataViewBitmapText: helper class used by BitmapTextRenderer -// ---------------------------------------------------------------------------- - -class DataViewBitmapText : public wxObject -{ -public: - DataViewBitmapText( const wxString &text = wxEmptyString, - const wxBitmap& bmp = wxNullBitmap) : - m_text(text), - m_bmp(bmp) - { } - - DataViewBitmapText(const DataViewBitmapText &other) - : wxObject(), - m_text(other.m_text), - m_bmp(other.m_bmp) - { } - - void SetText(const wxString &text) { m_text = text; } - wxString GetText() const { return m_text; } - void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; } - const wxBitmap &GetBitmap() const { return m_bmp; } - - bool IsSameAs(const DataViewBitmapText& other) const { - return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); - } - - bool operator==(const DataViewBitmapText& other) const { - return IsSameAs(other); - } - - bool operator!=(const DataViewBitmapText& other) const { - return !IsSameAs(other); - } - -private: - wxString m_text; - wxBitmap m_bmp; - - wxDECLARE_DYNAMIC_CLASS(DataViewBitmapText); -}; -DECLARE_VARIANT_OBJECT(DataViewBitmapText) - -// ---------------------------------------------------------------------------- -// BitmapTextRenderer -// ---------------------------------------------------------------------------- -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -class BitmapTextRenderer : public wxDataViewRenderer -#else -class BitmapTextRenderer : public wxDataViewCustomRenderer -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING -{ -public: - BitmapTextRenderer(wxWindow* parent, - wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - - , int align = wxDVR_DEFAULT_ALIGNMENT -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - ); -#else - ) : - wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), - m_parent(parent) - { -#ifdef SUPPORTS_MARKUP - m_markupText = nullptr; -#endif // SUPPORTS_MARKUP - } -#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - ~BitmapTextRenderer(); - -#ifdef SUPPORTS_MARKUP - void EnableMarkup(bool enable = true); -#endif // SUPPORTS_MARKUP - - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; -#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY - virtual wxString GetAccessibleDescription() const override; -#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING - - virtual bool Render(wxRect cell, wxDC* dc, int state) override; - virtual wxSize GetSize() const override; - - bool HasEditorCtrl() const override - { -#ifdef __WXOSX__ - return false; -#else - return true; -#endif - } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl(wxWindow* ctrl, - wxVariant& value) override; - bool WasCanceled() const { return m_was_unusable_symbol; } - -private: - DataViewBitmapText m_value; - bool m_was_unusable_symbol{ false }; - wxWindow* m_parent{ nullptr }; - -#ifdef SUPPORTS_MARKUP - class wxItemMarkupText* m_markupText; -#endif // SUPPORTS_MARKUP -}; - - -// ---------------------------------------------------------------------------- -// BitmapChoiceRenderer -// ---------------------------------------------------------------------------- - -class BitmapChoiceRenderer : public wxDataViewCustomRenderer -{ -public: - BitmapChoiceRenderer(wxDataViewCellMode mode = -#ifdef __WXOSX__ - wxDATAVIEW_CELL_INERT -#else - wxDATAVIEW_CELL_EDITABLE -#endif - , int align = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL - ) : wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) {} - - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; - - virtual bool Render(wxRect cell, wxDC* dc, int state) override; - virtual wxSize GetSize() const override; - - bool HasEditorCtrl() const override { return true; } - wxWindow* CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override; - bool GetValueFromEditorCtrl(wxWindow* ctrl, - wxVariant& value) override; - -private: - DataViewBitmapText m_value; -}; - - // ---------------------------------------------------------------------------- // ObjectDataViewModelNode: a node inside ObjectDataViewModel // ---------------------------------------------------------------------------- From 42f3bfb0f6a8df7f84a26c247fd1cd6620017200 Mon Sep 17 00:00:00 2001 From: Slic3rPE Date: Thu, 6 Aug 2020 10:56:14 +0200 Subject: [PATCH 04/25] Fixed a build under OSX --- src/slic3r/GUI/ExtraRenderers.cpp | 31 ++++++++++++++----------- src/slic3r/GUI/ExtraRenderers.hpp | 8 +++++-- src/slic3r/GUI/ObjectDataViewModel.cpp | 6 ----- src/slic3r/GUI/PresetComboBoxes.hpp | 1 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 6 ++--- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 494bfee6a..b49a3eb60 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -4,9 +4,11 @@ #include "I18N.hpp" #include +#ifdef wxHAS_GENERIC_DATAVIEWCTRL #include "wx/generic/private/markuptext.h" #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" +#endif #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY @@ -40,29 +42,30 @@ wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align) BitmapTextRenderer::~BitmapTextRenderer() { #ifdef SUPPORTS_MARKUP + #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) delete m_markupText; + #endif //wxHAS_GENERIC_DATAVIEWCTRL #endif // SUPPORTS_MARKUP } #ifdef SUPPORTS_MARKUP void BitmapTextRenderer::EnableMarkup(bool enable) { - if (enable) - { +#ifdef wxHAS_GENERIC_DATAVIEWCTRL + if (enable) { if (!m_markupText) - { m_markupText = new wxItemMarkupText(wxString()); - } } - else - { - if (m_markupText) - { + else { + if (m_markupText) { delete m_markupText; m_markupText = nullptr; } } +#elseif + is_markupText = enable +#endif //wxHAS_GENERIC_DATAVIEWCTRL } #endif // SUPPORTS_MARKUP @@ -70,10 +73,10 @@ bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL return true; } @@ -111,7 +114,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) xoffset = icon_sz.x + 4; } -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { int flags = 0; @@ -120,7 +123,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); } else -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL RenderText(m_value.GetText(), xoffset, rect, dc, state); return true; @@ -131,7 +134,7 @@ wxSize BitmapTextRenderer::GetSize() const if (!m_value.GetText().empty()) { wxSize size; -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { wxDataViewCtrl* const view = GetView(); @@ -142,7 +145,7 @@ wxSize BitmapTextRenderer::GetSize() const size = m_markupText->Measure(dc); } else -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL size = GetTextExtent(m_value.GetText()); int lines = m_value.GetText().Freq('\n') + 1; diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp index 96cf34945..41f0d7d32 100644 --- a/src/slic3r/GUI/ExtraRenderers.hpp +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -77,9 +77,9 @@ public: wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), m_parent(parent) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) m_markupText = nullptr; -#endif // SUPPORTS_MARKUP +#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -120,7 +120,11 @@ private: std::function can_create_editor_ctrl { nullptr }; #ifdef SUPPORTS_MARKUP + #ifdef wxHAS_GENERIC_DATAVIEWCTRL class wxItemMarkupText* m_markupText; + #elseif + bool is_markupText; + #endif #endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index a42073dd0..79fedfa52 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -9,12 +9,6 @@ #include #include -#include "wx/generic/private/markuptext.h" -#include "wx/generic/private/rowheightcache.h" -#include "wx/generic/private/widthcalc.h" -#if wxUSE_ACCESSIBILITY -#include "wx/private/markupparser.h" -#endif // wxUSE_ACCESSIBILITY namespace Slic3r { diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index f31b67fbe..7f51f775e 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -15,6 +15,7 @@ class ScalableButton; class wxBoxSizer; class wxComboBox; class wxStaticBitmap; +class wxRadioBox; namespace Slic3r { diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 46f086765..4db41ffaa 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -44,14 +44,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -133,7 +133,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#ifdef SUPPORTS_MARKUP +#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { std::string old_val = into_u8(str); From 4913378dbe5d5e3377a56690d836813a87102d66 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 15:54:12 +0200 Subject: [PATCH 05/25] Changed signature of the BitmapTextRenderer + Added experimental code for the rendering of the "markuped" text --- src/slic3r/GUI/ExtraRenderers.cpp | 30 ++++++++++++++++--------- src/slic3r/GUI/ExtraRenderers.hpp | 24 ++++++++------------ src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/Search.cpp | 2 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 17 ++++++-------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 2 +- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index b49a3eb60..046b8fa16 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -49,9 +49,9 @@ BitmapTextRenderer::~BitmapTextRenderer() #endif // SUPPORTS_MARKUP } -#ifdef SUPPORTS_MARKUP void BitmapTextRenderer::EnableMarkup(bool enable) { +#ifdef SUPPORTS_MARKUP #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (enable) { if (!m_markupText) @@ -63,20 +63,30 @@ void BitmapTextRenderer::EnableMarkup(bool enable) m_markupText = nullptr; } } -#elseif - is_markupText = enable +#else + is_markupText = enable; #endif //wxHAS_GENERIC_DATAVIEWCTRL -} #endif // SUPPORTS_MARKUP +} bool BitmapTextRenderer::SetValue(const wxVariant &value) { m_value << value; -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#ifdef SUPPORTS_MARKUP +#ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); -#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL +#else +#if defined(__WXGTK__) + GValue gvalue = G_VALUE_INIT; + g_value_init(&gvalue, G_TYPE_STRING); + g_value_set_string(&gvalue, wxGTK_CONV_FONT(str.GetText(), GetOwner()->GetOwner()->GetFont())); + g_object_set_property(G_OBJECT(m_renderer/*.GetText()*/), is_markupText ? "markup" : "text", &gvalue); + g_value_unset(&gvalue); +#endif // __WXGTK__ +#endif // wxHAS_GENERIC_DATAVIEWCTRL +#endif // SUPPORTS_MARKUP return true; } @@ -117,10 +127,8 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) #if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) if (m_markupText) { - int flags = 0; - rect.x += xoffset; - m_markupText->Render(GetView(), *dc, rect, flags, GetEllipsizeMode()); + m_markupText->Render(GetView(), *dc, rect, 0, GetEllipsizeMode()); } else #endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL @@ -161,7 +169,7 @@ wxSize BitmapTextRenderer::GetSize() const wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) { - if (!can_create_editor_ctrl()) + if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; DataViewBitmapText data; @@ -261,7 +269,7 @@ wxSize BitmapChoiceRenderer::GetSize() const wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRect, const wxVariant& value) { - if (!can_create_editor_ctrl()) + if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; std::vector icons = get_extruder_color_icons(); diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp index 41f0d7d32..4c1fb09de 100644 --- a/src/slic3r/GUI/ExtraRenderers.hpp +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -61,7 +61,7 @@ class BitmapTextRenderer : public wxDataViewCustomRenderer #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING { public: - BitmapTextRenderer(wxWindow* parent, + BitmapTextRenderer(bool use_markup = false, wxDataViewCellMode mode = #ifdef __WXOSX__ wxDATAVIEW_CELL_INERT @@ -73,24 +73,19 @@ public: #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING ); #else - ) : - wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align), - m_parent(parent) + ) : + wxDataViewCustomRenderer(wxT("DataViewBitmapText"), mode, align) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) - m_markupText = nullptr; -#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL + EnableMarkup(use_markup); } #endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING ~BitmapTextRenderer(); -#ifdef SUPPORTS_MARKUP void EnableMarkup(bool enable = true); -#endif // SUPPORTS_MARKUP - bool SetValue(const wxVariant& value); - bool GetValue(wxVariant& value) const; + bool SetValue(const wxVariant& value) override; + bool GetValue(wxVariant& value) const override; #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY virtual wxString GetAccessibleDescription() const override; #endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING @@ -115,15 +110,14 @@ public: private: DataViewBitmapText m_value; bool m_was_unusable_symbol{ false }; - wxWindow* m_parent{ nullptr }; std::function can_create_editor_ctrl { nullptr }; #ifdef SUPPORTS_MARKUP #ifdef wxHAS_GENERIC_DATAVIEWCTRL - class wxItemMarkupText* m_markupText; - #elseif - bool is_markupText; + class wxItemMarkupText* m_markupText { nullptr };; + #else + bool is_markupText {false}; #endif #endif // SUPPORTS_MARKUP }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index bbc0f5760..7648f7d23 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -277,7 +277,7 @@ void ObjectList::create_objects_ctrl() // column ItemName(Icon+Text) of the view control: // And Icon can be consisting of several bitmaps - BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(this); + BitmapTextRenderer* bmp_text_renderer = new BitmapTextRenderer(); bmp_text_renderer->set_can_create_editor_ctrl_function([this]() { return m_objects_model->GetItemType(GetSelection()) & (itVolume | itObject); }); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index d13ef469f..da9c8fe25 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -323,7 +323,7 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const const Option& OptionsSearcher::get_option(const std::string& opt_key) const { - auto it = std::upper_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); + auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); assert(it != options.end()); return options[it - options.begin()]; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 4db41ffaa..1bdc2959b 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -35,7 +35,7 @@ static const std::map type_icon_names = { {Preset::TYPE_SLA_PRINT, "cog" }, {Preset::TYPE_FILAMENT, "spool" }, {Preset::TYPE_SLA_MATERIAL, "resin" }, - {Preset::TYPE_PRINTER, "sla_printer" }, + {Preset::TYPE_PRINTER, "printer" }, }; static std::string black = "#000000"; @@ -427,7 +427,7 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const //------------------------------------------ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) - : DPIDialog(NULL, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); @@ -440,15 +440,12 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(/*L"\u2714"*/"", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em, wxALIGN_NOT);//2610,11,12 //2714 + m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - BitmapTextRenderer* renderer = new BitmapTextRenderer(m_tree); -#ifdef SUPPORTS_MARKUP - renderer->EnableMarkup(); -#endif - m_tree->AppendColumn(new wxDataViewColumn("", renderer, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - m_tree->AppendColumn(new wxDataViewColumn("Old value", renderer, UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); - m_tree->AppendColumn(new wxDataViewColumn("New value", renderer, UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 + + m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index c4a02d7bc..3d5867ea4 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -134,8 +134,8 @@ class UnsavedChangesModel : public wxDataViewModel public: enum { - colToggle, colIconText, + colToggle, colOldValue, colNewValue, colMax From 3688ae350b3b5728629fa5f8de3e37f95548ea68 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 16:28:12 +0200 Subject: [PATCH 06/25] Added missed includes for GTK --- src/slic3r/GUI/ExtraRenderers.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 046b8fa16..d6a1d7a99 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -9,6 +9,12 @@ #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" #endif + +#ifdef __WXGTK__ +#include "wx/gtk/private.h" +#include "wx/gtk/private/value.h" +#endif + #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY From 94efb5185bc1444a6adea9ad6f5ec9ca369f5e6e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 6 Aug 2020 16:54:14 +0200 Subject: [PATCH 07/25] One more experiment --- src/slic3r/GUI/ExtraRenderers.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index d6a1d7a99..e4c09dc26 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -9,12 +9,12 @@ #include "wx/generic/private/rowheightcache.h" #include "wx/generic/private/widthcalc.h" #endif - +/* #ifdef __WXGTK__ #include "wx/gtk/private.h" #include "wx/gtk/private/value.h" #endif - +*/ #if wxUSE_ACCESSIBILITY #include "wx/private/markupparser.h" #endif // wxUSE_ACCESSIBILITY @@ -83,14 +83,16 @@ bool BitmapTextRenderer::SetValue(const wxVariant &value) #ifdef wxHAS_GENERIC_DATAVIEWCTRL if (m_markupText) m_markupText->SetMarkup(m_value.GetText()); + /* #else #if defined(__WXGTK__) - GValue gvalue = G_VALUE_INIT; + GValue gvalue = G_VALUE_INIT; g_value_init(&gvalue, G_TYPE_STRING); g_value_set_string(&gvalue, wxGTK_CONV_FONT(str.GetText(), GetOwner()->GetOwner()->GetFont())); - g_object_set_property(G_OBJECT(m_renderer/*.GetText()*/), is_markupText ? "markup" : "text", &gvalue); + g_object_set_property(G_OBJECT(m_renderer/ *.GetText()* /), is_markupText ? "markup" : "text", &gvalue); g_value_unset(&gvalue); #endif // __WXGTK__ + */ #endif // wxHAS_GENERIC_DATAVIEWCTRL #endif // SUPPORTS_MARKUP From 08576ad7462f4724267fae4fcf5062e83b8b8586 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Aug 2020 10:21:20 +0200 Subject: [PATCH 08/25] UnsavedChangesDialog :: Toggle column is on first place now --- src/slic3r/GUI/ExtraRenderers.cpp | 6 +++--- src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 +--- src/slic3r/GUI/UnsavedChangesDialog.hpp | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index e4c09dc26..2915d498c 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -159,14 +159,14 @@ wxSize BitmapTextRenderer::GetSize() const dc.SetFont(GetAttr().GetEffectiveFont(view->GetFont())); size = m_markupText->Measure(dc); + + int lines = m_value.GetText().Freq('\n') + 1; + size.SetHeight(size.GetHeight() * lines); } else #endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL size = GetTextExtent(m_value.GetText()); - int lines = m_value.GetText().Freq('\n') + 1; - size.SetHeight(size.GetHeight() * lines); - if (m_value.GetBitmap().IsOk()) size.x += m_value.GetBitmap().GetWidth() + 4; return size; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 1bdc2959b..ab2c36ebd 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -440,10 +440,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 - + m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 3d5867ea4..c4a02d7bc 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -134,8 +134,8 @@ class UnsavedChangesModel : public wxDataViewModel public: enum { - colIconText, colToggle, + colIconText, colOldValue, colNewValue, colMax From c4569c93f2afc988de3c8b11fd1c9ce51af41b55 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Aug 2020 15:09:58 +0200 Subject: [PATCH 09/25] UnsavedChangesDialog: Fixed get_string_from_enum() in respect to the InfillPattern --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 47 ++++++++++++++++--------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 11 ------ 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index ab2c36ebd..3f85ac742 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -2,11 +2,9 @@ #include #include +#include #include #include -#include - -#include "wx/dataview.h" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/PresetBundle.hpp" @@ -15,8 +13,8 @@ #include "Tab.hpp" #include "ObjectDataViewModel.hpp" -#define FTS_FUZZY_MATCH_IMPLEMENTATION -#include "fts_fuzzy_match.h" +//#define FTS_FUZZY_MATCH_IMPLEMENTATION +//#include "fts_fuzzy_match.h" #include "BitmapCache.hpp" @@ -195,8 +193,10 @@ ModelNode* UnsavedChangesModel::AddOption(ModelNode* group_node, wxString option { ModelNode* option = new ModelNode(group_node, option_name, old_value, new_value); group_node->Append(option); - ItemAdded(wxDataViewItem((void*)group_node), wxDataViewItem((void*)option)); + wxDataViewItem group_item = wxDataViewItem((void*)group_node); + ItemAdded(group_item, wxDataViewItem((void*)option)); + m_ctrl->Expand(group_item); return option; } @@ -204,9 +204,7 @@ ModelNode* UnsavedChangesModel::AddOptionWithGroup(ModelNode* category_node, wxS { ModelNode* group_node = new ModelNode(category_node, group_name); category_node->Append(group_node); - wxDataViewItem group_item = wxDataViewItem((void*)group_node); - ItemAdded(wxDataViewItem((void*)category_node), group_item); - m_ctrl->Expand(group_item); + ItemAdded(wxDataViewItem((void*)category_node), wxDataViewItem((void*)group_node)); return AddOption(group_node, option_name, old_value, new_value); } @@ -435,16 +433,19 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) int border = 10; int em = em_unit(); - m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES); m_tree_model = new UnsavedChangesModel(this); m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 - m_tree->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE)); + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + m_tree->AppendColumn(icon_text_clmn); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->SetExpanderColumn(icon_text_clmn); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -498,11 +499,25 @@ void UnsavedChangesDialog::close(Action action) } template -wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config) +wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConfig& config, bool is_infill = false) { - const std::vector& names = config.def()->options.at(opt_key).enum_labels;//ConfigOptionEnum::get_enum_names(); + const ConfigOptionDef& def = config.def()->options.at(opt_key); + const std::vector& names = def.enum_labels;//ConfigOptionEnum::get_enum_names(); T val = config.option>(opt_key)->value; - return from_u8(_u8L(names[static_cast(val)])); + + // Each infill doesn't use all list of infill declared in PrintConfig.hpp. + // So we should "convert" val to the correct one + if (is_infill) { + for (auto key_val : *def.enum_keys_map) + if ((int)key_val.second == (int)val) { + auto it = std::find(def.enum_values.begin(), def.enum_values.end(), key_val.first); + if (it == def.enum_values.end()) + return ""; + return from_u8(_utf8(names[it - def.enum_values.begin()])); + } + return _L("Undef"); + } + return from_u8(_utf8(names[static_cast(val)])); } static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) @@ -575,7 +590,7 @@ static wxString get_string_value(const std::string& opt_key, const DynamicPrintC if (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern") - return get_string_from_enum(opt_key, config); + return get_string_from_enum(opt_key, config, true); if (opt_key == "gcode_flavor") return get_string_from_enum(opt_key, config); if (opt_key == "ironing_type") diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index c4a02d7bc..8afd97896 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -1,19 +1,8 @@ #ifndef slic3r_UnsavedChangesDialog_hpp_ #define slic3r_UnsavedChangesDialog_hpp_ -#include -#include - -#include -#include -#include #include -#include - -#include -#include - #include "GUI_Utils.hpp" #include "wxExtensions.hpp" #include "libslic3r/Preset.hpp" From 8b74ae4568db08dd1039204291b9b83e2842b7b4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 09:45:32 +0200 Subject: [PATCH 10/25] Use the wxDataViewIconTextRenderer instead of the DataViewBitmapTextRenderer under GTK --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 37 ++++++++++++++++++++++--- src/slic3r/GUI/UnsavedChangesDialog.hpp | 10 +++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 3f85ac742..33f6f19de 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -42,14 +42,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && /*!defined(__APPLE__)*/defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)//defined(wxHAS_GENERIC_DATAVIEWCTRL) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -60,7 +60,11 @@ ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : m_preset_type(preset_type), m_text(text) { +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(type_icon_names.at(preset_type))); +#else m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); +#endif //__linux__ } // group node @@ -68,7 +72,11 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& m_parent(parent), m_text(text) { +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); +#else m_icon = create_scaled_bitmap(icon_name); +#endif //__linux__ } // category node @@ -300,7 +308,11 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite variant = node->m_toggle; break; case colIconText: +#ifdef __linux__ + variant << wxDataViewIconText(node->m_text, node->m_icon); +#else variant << DataViewBitmapText(node->m_text, node->m_icon); +#endif //__linux__ break; case colOldValue: variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); @@ -322,10 +334,18 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte switch (col) { case colIconText: { +#ifdef __linux__ + wxDataViewIconText data; +#else DataViewBitmapText data; +#endif //__linux__ data << variant; - node->m_icon = data.GetBitmap(); node->m_text = data.GetText(); +#ifdef __linux__ + node->m_icon = data.GetIcon(); +#else + node->m_icon = data.GetBitmap(); +#endif //__linux__ return true; } case colToggle: node->m_toggle = variant.GetBool(); @@ -439,7 +459,16 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree_model->SetAssociatedControl(m_tree); m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + +#ifdef __linux__ + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); +#ifdef SUPPORTS_MARKUP + rd->EnableMarkup(true); +#endif + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", rd, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); +#else + wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); +#endif //__linux__ m_tree->AppendColumn(icon_text_clmn); m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 8afd97896..29926cb24 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -18,6 +18,12 @@ namespace GUI{ class ModelNode; WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); +// On all of 3 different platforms Bitmap+Text icon column looks different +// because of Markup text is missed or not implemented. +// As a temporary workaround, we will use: +// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text) +// OSX - -//-, but Markup text is not implemented right now +// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) class ModelNode { wxWindow* m_parent_win{ nullptr }; @@ -47,7 +53,11 @@ class ModelNode public: bool m_toggle {true}; +#ifdef __linux__ + wxIcon m_icon; +#else wxBitmap m_icon; +#endif //__linux__ wxBitmap m_old_color_bmp; wxBitmap m_new_color_bmp; wxString m_text; From f87ca111e15dce1d63c5d78045236698659148ff Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 11:24:31 +0200 Subject: [PATCH 11/25] GTK specific: Use the wxDataViewIconTextRenderer instead of the DataViewBitmapRenderer for "Old/NewValue" columns too. + update ofthe enabling for the "Save/Move" buttons --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 110 ++++++++++++++++++------ src/slic3r/GUI/UnsavedChangesDialog.hpp | 26 ++++-- 2 files changed, 106 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 33f6f19de..cf1bc13e5 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -42,14 +42,14 @@ static std::string orange = "#ed6b21"; static void color_string(wxString& str, const std::string& color) { -#if defined(SUPPORTS_MARKUP) && /*!defined(__APPLE__)*/defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) str = from_u8((boost::format("%2%") % color % into_u8(str)).str()); #endif } static void make_string_bold(wxString& str) { -#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__)//defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) str = from_u8((boost::format("%1%") % into_u8(str)).str()); #endif } @@ -86,7 +86,11 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text) : { } +#ifdef __linux__ +wxIcon ModelNode::get_bitmap(const wxString& color); +#else wxBitmap ModelNode::get_bitmap(const wxString& color) +#endif // __linux__ { /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. * So set sizes for solid_colored icons used for filament preset @@ -100,7 +104,16 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) unsigned char rgb[3]; BitmapCache::parse_color(into_u8(color), rgb); // there is no need to scale created solid bitmap - return bmp_cache.mksolid(icon_width, icon_height, rgb, true); + wxBitmap bmp = bmp_cache.mksolid(icon_width, icon_height, rgb, true); + +#ifdef __linux__ + wxIcon icon; + icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); + return icon; +#else + return bmp; +#endif // __linux__ + } // option node @@ -297,6 +310,13 @@ void UnsavedChangesModel::UpdateItemEnabling(wxDataViewItem item) update_parents(node); } +bool UnsavedChangesModel::IsEnabledItem(const wxDataViewItem& item) +{ + assert(item.IsOk()); + ModelNode* node = (ModelNode*)item.GetID(); + return node->IsToggled(); +} + void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const { assert(item.IsOk()); @@ -307,12 +327,19 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colToggle: variant = node->m_toggle; break; - case colIconText: #ifdef __linux__ + case colIconText: variant << wxDataViewIconText(node->m_text, node->m_icon); + break; + case colOldValue: + variant << wxDataViewIconText(node->m_old_value, node->m_old_color_bmp); + break; + case colNewValue: + variant << wxDataViewIconText(node->m_new_value, node->m_new_color_bmp); + break; #else + case colIconText: variant << DataViewBitmapText(node->m_text, node->m_icon); -#endif //__linux__ break; case colOldValue: variant << DataViewBitmapText(node->m_old_value, node->m_old_color_bmp); @@ -320,6 +347,7 @@ void UnsavedChangesModel::GetValue(wxVariant& variant, const wxDataViewItem& ite case colNewValue: variant << DataViewBitmapText(node->m_new_value, node->m_new_color_bmp); break; +#endif //__linux__ default: wxLogError("UnsavedChangesModel::GetValue: wrong column %d", col); @@ -333,23 +361,35 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte ModelNode* node = (ModelNode*)item.GetID(); switch (col) { - case colIconText: { -#ifdef __linux__ - wxDataViewIconText data; -#else - DataViewBitmapText data; -#endif //__linux__ - data << variant; - node->m_text = data.GetText(); -#ifdef __linux__ - node->m_icon = data.GetIcon(); -#else - node->m_icon = data.GetBitmap(); -#endif //__linux__ - return true; } case colToggle: node->m_toggle = variant.GetBool(); return true; +#ifdef __linux__ + case colIconText: { + wxDataViewIconText data; + data << variant; + node->m_icon = data.GetIcon(); + node->m_text = data.GetText(); + return true; } + case colOldValue: { + wxDataViewIconText data; + data << variant; + node->m_old_color_bmp = data.GetIcon(); + node->m_old_value = data.GetText(); + return true; } + case colNewValue: { + wxDataViewIconText data; + data << variant; + node->m_new_color_bmp = data.GetIcon(); + node->m_new_value = data.GetText(); + return true; } +#else + case colIconText: { + DataViewBitmapText data; + data << variant; + node->m_icon = data.GetBitmap(); + node->m_text = data.GetText(); + return true; } case colOldValue: { DataViewBitmapText data; data << variant; @@ -362,6 +402,7 @@ bool UnsavedChangesModel::SetValue(const wxVariant& variant, const wxDataViewIte node->m_new_color_bmp = data.GetBitmap(); node->m_new_value = data.GetText(); return true; } +#endif //__linux__ default: wxLogError("UnsavedChangesModel::SetValue: wrong column"); } @@ -458,7 +499,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AssociateModel(m_tree_model); m_tree_model->SetAssociatedControl(m_tree); - m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE/*, 6 * em*/);//2610,11,12 //2714 + m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 #ifdef __linux__ wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); @@ -473,7 +514,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); - m_tree->SetExpanderColumn(icon_text_clmn); +// m_tree->SetExpanderColumn(icon_text_clmn); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); @@ -486,14 +527,18 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); - save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); buttons->Insert(0, save_btn, 0, wxLEFT, 5); + save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); - move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); buttons->Insert(1, move_btn, 0, wxLEFT, 5); + move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); buttons->Insert(2, continue_btn, 0, wxLEFT, 5); @@ -519,6 +564,9 @@ void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) m_tree_model->UpdateItemEnabling(item); m_tree->Refresh(); + + // update an enabling of the "save/move" buttons + m_empty_selection = get_selected_options().empty(); } void UnsavedChangesDialog::close(Action action) @@ -670,12 +718,24 @@ void UnsavedChangesDialog::update(Preset::Type type) for (const std::string& opt_key : presets->current_dirty_options()) { const Search::Option& option = searcher.get_option(opt_key); - m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, - get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)); + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, + get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)), opt_key); } } +std::vector UnsavedChangesDialog::get_selected_options() +{ + std::vector ret; + + for (auto item : m_items_map) { + if (m_tree_model->IsEnabledItem(item.first)) + ret.emplace_back(item.second); + } + + return ret; +} + void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 29926cb24..5c23da997 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -2,6 +2,8 @@ #define slic3r_UnsavedChangesDialog_hpp_ #include +#include +#include #include "GUI_Utils.hpp" #include "wxExtensions.hpp" @@ -48,18 +50,24 @@ class ModelNode // would be added to the control) bool m_container {true}; +#ifdef __linux__ + wxIcon get_bitmap(const wxString& color); +#else wxBitmap get_bitmap(const wxString& color); +#endif //__linux__ public: bool m_toggle {true}; #ifdef __linux__ wxIcon m_icon; + wxIcon m_old_color_bmp; + wxIcon m_new_color_bmp; #else wxBitmap m_icon; -#endif //__linux__ wxBitmap m_old_color_bmp; wxBitmap m_new_color_bmp; +#endif //__linux__ wxString m_text; wxString m_old_value; wxString m_new_value; @@ -150,6 +158,7 @@ public: wxString old_value, wxString new_value); void UpdateItemEnabling(wxDataViewItem item); + bool IsEnabledItem(const wxDataViewItem& item); unsigned int GetColumnCount() const override { return colMax; } wxString GetColumnType(unsigned int col) const override; @@ -176,15 +185,20 @@ class UnsavedChangesDialog : public DPIDialog wxDataViewCtrl* m_tree { nullptr }; UnsavedChangesModel* m_tree_model { nullptr }; - int m_save_btn_id { wxID_ANY }; - int m_move_btn_id { wxID_ANY }; - int m_continue_btn_id { wxID_ANY }; + bool m_empty_selection { false }; + int m_save_btn_id { wxID_ANY }; + int m_move_btn_id { wxID_ANY }; + int m_continue_btn_id { wxID_ANY }; enum class Action { + Undef, Save, Move, Continue - } m_action; + } m_action {Action::Undef}; + + + std::map m_items_map; public: UnsavedChangesDialog(Preset::Type type); @@ -198,6 +212,8 @@ public: bool move_preset() const { return m_action == Action::Move; } bool just_continue() const { return m_action == Action::Continue; } + std::vector get_selected_options(); + protected: void on_dpi_changed(const wxRect& suggested_rect) override; void on_sys_color_changed() override; From 11c22e7fb2f38adc17d3007960f108c97700adb6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 11:41:19 +0200 Subject: [PATCH 12/25] Fixed Linux build --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index cf1bc13e5..122e34e02 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -87,7 +87,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text) : } #ifdef __linux__ -wxIcon ModelNode::get_bitmap(const wxString& color); +wxIcon ModelNode::get_bitmap(const wxString& color) #else wxBitmap ModelNode::get_bitmap(const wxString& color) #endif // __linux__ @@ -113,7 +113,6 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) #else return bmp; #endif // __linux__ - } // option node From 058e024d2dbe8d2b2d0d127e54af171cb7871c48 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 10 Aug 2020 19:07:45 +0200 Subject: [PATCH 13/25] Implemented FullCompareDialog for show long string values + fixed build under GTK --- src/slic3r/GUI/Tab.cpp | 4 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 194 ++++++++++++++++++++---- src/slic3r/GUI/UnsavedChangesDialog.hpp | 51 ++++++- src/slic3r/GUI/wxExtensions.cpp | 16 +- src/slic3r/GUI/wxExtensions.hpp | 6 +- 5 files changed, 231 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4a250dc52..6501fba27 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2990,7 +2990,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, bool canceled = false; bool technology_changed = false; m_dependent_tabs.clear(); - if (current_dirty && ! may_discard_current_dirty_preset()) { + if (current_dirty && ! may_discard_current_dirty_preset(nullptr, preset_name)) { canceled = true; } else if (print_tab) { // Before switching the print profile to a new one, verify, whether the currently active filament or SLA material @@ -3132,7 +3132,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { - UnsavedChangesDialog dlg(m_type); + UnsavedChangesDialog dlg(m_type, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; if (dlg.just_continue()) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 122e34e02..78d599f7a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -11,7 +11,8 @@ #include "GUI_App.hpp" #include "Plater.hpp" #include "Tab.hpp" -#include "ObjectDataViewModel.hpp" +#include "ExtraRenderers.hpp" +#include "wxExtensions.hpp" //#define FTS_FUZZY_MATCH_IMPLEMENTATION //#include "fts_fuzzy_match.h" @@ -104,14 +105,12 @@ wxBitmap ModelNode::get_bitmap(const wxString& color) unsigned char rgb[3]; BitmapCache::parse_color(into_u8(color), rgb); // there is no need to scale created solid bitmap - wxBitmap bmp = bmp_cache.mksolid(icon_width, icon_height, rgb, true); - -#ifdef __linux__ - wxIcon icon; - icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); - return icon; +#ifndef __linux__ + return bmp_cache.mksolid(icon_width, icon_height, rgb, true); #else - return bmp; + wxIcon icon; + icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true)); + return icon; #endif // __linux__ } @@ -484,7 +483,7 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const // UnsavedChangesDialog //------------------------------------------ -UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -516,6 +515,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) // m_tree->SetExpanderColumn(icon_text_clmn); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); + m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); + m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -525,31 +526,42 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type) PresetCollection* presets = tab->get_presets(); wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); - auto save_btn = new wxButton(this, m_save_btn_id = NewControlId(), label); - buttons->Insert(0, save_btn, 0, wxLEFT, 5); + m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); - save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"NewSelectedPreset\"")).str()); - auto move_btn = new wxButton(this, m_move_btn_id = NewControlId(), label); - buttons->Insert(1, move_btn, 0, wxLEFT, 5); + label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); + m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_MOTION, [this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); - auto continue_btn = new wxButton(this, m_continue_btn_id = NewControlId(), _L("Continue without changes")); - continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - buttons->Insert(2, continue_btn, 0, wxLEFT, 5); + label = _L("Continue without changes"); + m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); + m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + m_continue_btn->Bind(wxEVT_MOTION, [this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + + m_info_line = new wxStaticText(this, wxID_ANY, ""); + m_info_line->SetFont(wxGetApp().bold_font()); + m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There is unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); update(type); + this->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); + SetSizer(topSizer); topSizer->SetSizeHints(this); } @@ -568,9 +580,61 @@ void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) m_empty_selection = get_selected_options().empty(); } +void UnsavedChangesDialog::context_menu(wxDataViewEvent& event) +{ + if (!m_has_long_strings) + return; + + wxDataViewItem item = event.GetItem(); + if (!item) + { + wxPoint mouse_pos = wxGetMousePosition() - m_tree->GetScreenPosition(); + wxDataViewColumn* col = nullptr; + m_tree->HitTest(mouse_pos, item, col); + + if (!item) + item = m_tree->GetSelection(); + + if (!item) + return; + } + + auto it = m_items_map.find(item); + if (it == m_items_map.end() || !it->second.is_long) + return; + FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal(); +} + +void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name) +{ + if (m_motion_action == action) + return; + if (action == Action::Undef && !m_has_long_strings) + m_info_line->Hide(); + else { + wxString text; + if (action == Action::Undef) + text = _L("Some fields are too long to fit. Right click on it to show full text."); + else if (action == Action::Continue) + text = _L("All changed options will be reverted."); + else { + std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + text += "\n" + _L("Unselected options will be reverted."); + } + m_info_line->SetLabel(text); + m_info_line->Show(); + } + + m_motion_action = action; + + Layout(); + Refresh(); +} + void UnsavedChangesDialog::close(Action action) { - m_action = action; + m_exit_action = action; this->EndModal(wxID_CLOSE); } @@ -698,6 +762,23 @@ static wxString get_string_value(const std::string& opt_key, const DynamicPrintC return out; } +wxString UnsavedChangesDialog::get_short_string(wxString full_string) +{ + int max_len = 30; + if (full_string.IsEmpty() || full_string.StartsWith("#") || + (full_string.Find("\n") == wxNOT_FOUND && full_string.Length() < max_len)) + return full_string; + + m_has_long_strings = true; + + int n_pos = full_string.Find("\n"); + if (n_pos != wxNOT_FOUND && n_pos < max_len) + max_len = n_pos; + + full_string.Truncate(max_len); + return full_string + dots; +} + void UnsavedChangesDialog::update(Preset::Type type) { Tab* tab = wxGetApp().get_tab(type); @@ -717,8 +798,14 @@ void UnsavedChangesDialog::update(Preset::Type type) for (const std::string& opt_key : presets->current_dirty_options()) { const Search::Option& option = searcher.get_option(opt_key); - m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, - get_string_value(opt_key, old_config), get_string_value(opt_key, new_config)), opt_key); + ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config) }; + + wxString old_val = get_short_string(item_data.old_val); + wxString new_val = get_short_string(item_data.new_val); + if (old_val != item_data.old_val || new_val != item_data.new_val) + item_data.is_long = true; + + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); } } @@ -727,10 +814,9 @@ std::vector UnsavedChangesDialog::get_selected_options() { std::vector ret; - for (auto item : m_items_map) { + for (auto item : m_items_map) if (m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second); - } + ret.emplace_back(item.second.opt_key); return ret; } @@ -757,6 +843,58 @@ void UnsavedChangesDialog::on_sys_color_changed() } +//------------------------------------------ +// FullCompareDialog +//------------------------------------------ + +FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value) + : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SetBackgroundColour(bgr_clr); + + int border = 10; + + wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this); + + wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(2, 2, 1, 0); + grid_sizer->SetFlexibleDirection(wxBOTH); + grid_sizer->AddGrowableCol(0,1); + grid_sizer->AddGrowableCol(1,1); + grid_sizer->AddGrowableRow(1,1); + + auto add_header = [grid_sizer, border, this](wxString label) { + wxStaticText* text = new wxStaticText(this, wxID_ANY, label); + text->SetFont(wxGetApp().bold_font()); + grid_sizer->Add(text, 0, wxALL, border); + }; + + auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { + wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE); + if (is_colored) + text->SetForegroundColour(wxColour(orange)); + grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); + }; + + add_header(_L("Old value")); + add_header(_L("New value")); + add_value(old_value); + add_value(new_value, true); + + sizer->Add(grid_sizer, 1, wxEXPAND); + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(sizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); +} + + } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 5c23da997..991c89442 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -9,8 +9,10 @@ #include "wxExtensions.hpp" #include "libslic3r/Preset.hpp" -namespace Slic3r { +class ScalableButton; +class wxStaticText; +namespace Slic3r { namespace GUI{ // ---------------------------------------------------------------------------- @@ -185,7 +187,13 @@ class UnsavedChangesDialog : public DPIDialog wxDataViewCtrl* m_tree { nullptr }; UnsavedChangesModel* m_tree_model { nullptr }; + ScalableButton* m_save_btn { nullptr }; + ScalableButton* m_move_btn { nullptr }; + ScalableButton* m_continue_btn { nullptr }; + wxStaticText* m_info_line { nullptr }; + bool m_empty_selection { false }; + bool m_has_long_strings { false }; int m_save_btn_id { wxID_ANY }; int m_move_btn_id { wxID_ANY }; int m_continue_btn_id { wxID_ANY }; @@ -195,22 +203,40 @@ class UnsavedChangesDialog : public DPIDialog Save, Move, Continue - } m_action {Action::Undef}; + }; + // selected action after Dialog closing + Action m_exit_action {Action::Undef}; - std::map m_items_map; + // Action during mouse motion + Action m_motion_action {Action::Undef}; + + struct ItemData + { + std::string opt_key; + wxString opt_name; + wxString old_val; + wxString new_val; + bool is_long {false}; + }; + // tree items related to the options + std::map m_items_map; public: - UnsavedChangesDialog(Preset::Type type); + UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} + wxString get_short_string(wxString full_string); + void update(Preset::Type type); void item_value_changed(wxDataViewEvent &event); + void context_menu(wxDataViewEvent &event); + void show_info_line(Action action, std::string preset_name = ""); void close(Action action); - bool save_preset() const { return m_action == Action::Save; } - bool move_preset() const { return m_action == Action::Move; } - bool just_continue() const { return m_action == Action::Continue; } + bool save_preset() const { return m_exit_action == Action::Save; } + bool move_preset() const { return m_exit_action == Action::Move; } + bool just_continue() const { return m_exit_action == Action::Continue; } std::vector get_selected_options(); @@ -220,6 +246,17 @@ protected: }; +//------------------------------------------ +// FullCompareDialog +//------------------------------------------ +class FullCompareDialog : public wxDialog +{ +public: + FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value); + ~FullCompareDialog() {} +}; + + } } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 67b5a18f7..0cf09b4ae 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -782,9 +782,11 @@ ScalableButton::ScalableButton( wxWindow * parent, const wxString& label /* = wxEmptyString*/, const wxSize& size /* = wxDefaultSize*/, const wxPoint& pos /* = wxDefaultPosition*/, - long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) : + long style /*= wxBU_EXACTFIT | wxNO_BORDER*/, + bool use_default_disabled_bitmap/* = false*/) : + m_parent(parent), m_current_icon_name(icon_name), - m_parent(parent) + m_use_default_disabled_bitmap (use_default_disabled_bitmap) { Create(parent, id, label, pos, size, style); #ifdef __WXMSW__ @@ -793,6 +795,8 @@ ScalableButton::ScalableButton( wxWindow * parent, #endif // __WXMSW__ SetBitmap(create_scaled_bitmap(icon_name, parent)); + if (m_use_default_disabled_bitmap) + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); if (size != wxDefaultSize) { @@ -842,11 +846,19 @@ int ScalableButton::GetBitmapHeight() #endif } +void ScalableButton::UseDefaultBitmapDisabled() +{ + m_use_default_disabled_bitmap = true; + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); +} + void ScalableButton::msw_rescale() { SetBitmap(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt)); if (!m_disabled_icon_name.empty()) SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt)); + else if (m_use_default_disabled_bitmap) + SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); if (m_width > 0 || m_height>0) { diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 9be3361bd..8fe28b2e5 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -210,7 +210,8 @@ public: const wxString& label = wxEmptyString, const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition, - long style = wxBU_EXACTFIT | wxNO_BORDER); + long style = wxBU_EXACTFIT | wxNO_BORDER, + bool use_default_disabled_bitmap = false); ScalableButton( wxWindow * parent, @@ -224,6 +225,7 @@ public: void SetBitmap_(const ScalableBitmap& bmp); void SetBitmapDisabled_(const ScalableBitmap &bmp); int GetBitmapHeight(); + void UseDefaultBitmapDisabled(); void msw_rescale(); @@ -234,6 +236,8 @@ private: int m_width {-1}; // should be multiplied to em_unit int m_height{-1}; // should be multiplied to em_unit + bool m_use_default_disabled_bitmap {false}; + // bitmap dimensions int m_px_cnt{ 16 }; }; From 6a33c967cfa62290b932be44b3f174f6ff27e067 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Aug 2020 09:17:52 +0200 Subject: [PATCH 14/25] Fixed GTK build --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 38 ++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 78d599f7a..dcd1d760a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -499,33 +499,38 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->AppendToggleColumn(L"\u2714", UnsavedChangesModel::colToggle, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);//2610,11,12 //2714 + auto append_bmp_text_column = [this](const wxString& label, unsigned model_column, int width, bool set_expander = false) + { #ifdef __linux__ - wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); #ifdef SUPPORTS_MARKUP - rd->EnableMarkup(true); + rd->EnableMarkup(true); #endif - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", rd, UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); + wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); #else - wxDataViewColumn* icon_text_clmn = new wxDataViewColumn("", new BitmapTextRenderer(true), UnsavedChangesModel::colIconText, 30 * em, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); + wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true), model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); #endif //__linux__ - m_tree->AppendColumn(icon_text_clmn); - m_tree->AppendColumn(new wxDataViewColumn("Old value", new BitmapTextRenderer(true), UnsavedChangesModel::colOldValue, 20 * em, wxALIGN_TOP)); - m_tree->AppendColumn(new wxDataViewColumn("New value", new BitmapTextRenderer(true), UnsavedChangesModel::colNewValue, 20 * em, wxALIGN_TOP)); + m_tree->AppendColumn(column); + if (set_expander) + m_tree->SetExpanderColumn(column); + }; -// m_tree->SetExpanderColumn(icon_text_clmn); + append_bmp_text_column("", UnsavedChangesModel::colIconText, 30 * em); + append_bmp_text_column(_L("Old Value"), UnsavedChangesModel::colOldValue, 20 * em); + append_bmp_text_column(_L("New Value"), UnsavedChangesModel::colNewValue, 20 * em); m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + // Add Buttons + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); Tab* tab = wxGetApp().get_tab(type); assert(tab); - PresetCollection* presets = tab->get_presets(); - wxString label= from_u8((boost::format(_u8L("Save selected to preset:%1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); + wxString label= from_u8((boost::format(_u8L("Save selected to preset: %1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); @@ -533,7 +538,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - label = from_u8((boost::format(_u8L("Move selected to preset:%1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); + label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); @@ -549,14 +554,13 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_info_line = new wxStaticText(this, wxID_ANY, ""); m_info_line->SetFont(wxGetApp().bold_font()); - m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); - topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); - topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); + topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); + topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); update(type); @@ -564,6 +568,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& SetSizer(topSizer); topSizer->SetSizeHints(this); + + show_info_line(Action::Undef); } void UnsavedChangesDialog::item_value_changed(wxDataViewEvent& event) From cb407e46e5d833d5e286d4e7e2f092fb3f19e6ec Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 11 Aug 2020 10:37:08 +0200 Subject: [PATCH 15/25] Fixed color update under GTK + FullCompareDialog : Use SetStile instead of SetForegroundColour for the wxTextCtrls --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index dcd1d760a..9f9ec3d9f 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -150,7 +150,7 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL) +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { std::string old_val = into_u8(str); @@ -876,9 +876,10 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString }; auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { - wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE); + wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); if (is_colored) - text->SetForegroundColour(wxColour(orange)); +// text->SetForegroundColour(wxColour(orange)); + text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; From 6dafdc5bab81b45a26a56d0e32fdfd035c06461b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Aug 2020 13:36:26 +0200 Subject: [PATCH 16/25] Fixed rescaling under MSW --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 116 +++++++++++++++++------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 9 +- 2 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 9f9ec3d9f..53dcf3f02 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -56,32 +56,29 @@ static void make_string_bold(wxString& str) } // preset(root) node -ModelNode::ModelNode(Preset::Type preset_type, const wxString& text) : +ModelNode::ModelNode(Preset::Type preset_type, const wxString& text, wxWindow* parent_win) : + m_parent_win(parent_win), m_parent(nullptr), m_preset_type(preset_type), + m_icon_name(type_icon_names.at(preset_type)), m_text(text) { -#ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(type_icon_names.at(preset_type))); -#else - m_icon = create_scaled_bitmap(type_icon_names.at(preset_type)); -#endif //__linux__ + UpdateIcons(); } // group node ModelNode::ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name) : + m_parent_win(parent->m_parent_win), m_parent(parent), + m_icon_name(icon_name), m_text(text) { -#ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(icon_name)); -#else - m_icon = create_scaled_bitmap(icon_name); -#endif //__linux__ + UpdateIcons(); } // category node ModelNode::ModelNode(ModelNode* parent, const wxString& text) : + m_parent_win(parent->m_parent_win), m_parent(parent), m_text(text) { @@ -150,12 +147,13 @@ ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& ol void ModelNode::UpdateEnabling() { -#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) auto change_text_color = [](wxString& str, const std::string& clr_from, const std::string& clr_to) { +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) std::string old_val = into_u8(str); boost::replace_all(old_val, clr_from, clr_to); str = from_u8(old_val); +#endif }; if (!m_toggle) { @@ -168,12 +166,27 @@ void ModelNode::UpdateEnabling() change_text_color(m_old_value, grey, black); change_text_color(m_new_value, grey, orange); } -#endif // update icons for the colors + UpdateIcons(); +} + +void ModelNode::UpdateIcons() +{ + // update icons for the colors, if any exists if (!m_old_color.IsEmpty()) - m_old_color_bmp = get_bitmap(m_toggle? m_old_color : grey); + m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : grey); if (!m_new_color.IsEmpty()) - m_new_color_bmp = get_bitmap(m_toggle? m_new_color : grey); + m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : grey); + + // update main icon, if any exists + if (m_icon_name.empty()) + return; + +#ifdef __linux__ + m_icon.CopyFromBitmap(create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle)); +#else + m_icon = create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle); +#endif //__linux__ } @@ -198,7 +211,7 @@ wxDataViewItem UnsavedChangesModel::AddPreset(Preset::Type type, wxString preset color_string(preset_name, black); make_string_bold(preset_name); - auto preset = new ModelNode(type, preset_name); + auto preset = new ModelNode(type, preset_name, m_parent_win); m_preset_nodes.emplace_back(preset); wxDataViewItem child((void*)preset); @@ -478,6 +491,24 @@ wxString UnsavedChangesModel::GetColumnType(unsigned int col) const } } +static void rescale_children(ModelNode* parent) +{ + if (parent->IsContainer()) { + for (ModelNode* child : parent->GetChildren()) { + child->UpdateIcons(); + rescale_children(child); + } + } +} + +void UnsavedChangesModel::Rescale() +{ + for (ModelNode* node : m_preset_nodes) { + node->UpdateIcons(); + rescale_children(node); + } +} + //------------------------------------------ // UnsavedChangesDialog @@ -489,6 +520,13 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); +#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + // ys_FIXME! temporary workaround for correct font scaling + // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, + // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT + this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + int border = 10; int em = em_unit(); @@ -521,7 +559,6 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &UnsavedChangesDialog::item_value_changed, this); m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); - m_tree->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); // Add Buttons wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); @@ -534,26 +571,30 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); - m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_save_btn->Bind(wxEVT_MOTION, [this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); + m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_save_btn->Bind(wxEVT_ENTER_WINDOW,[this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_move_btn->Bind(wxEVT_MOTION, [this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); + m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + m_move_btn->Bind(wxEVT_ENTER_WINDOW,[this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_move_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); label = _L("Continue without changes"); m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); - m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - m_continue_btn->Bind(wxEVT_MOTION, [this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); + m_continue_btn->Bind(wxEVT_ENTER_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); + m_continue_btn->Bind(wxEVT_LEAVE_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); m_info_line = new wxStaticText(this, wxID_ANY, ""); - m_info_line->SetFont(wxGetApp().bold_font()); + m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_info_line->Hide(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); @@ -564,8 +605,6 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& update(type); - this->Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); - SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -829,21 +868,34 @@ std::vector UnsavedChangesDialog::get_selected_options() void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) { - const int& em = em_unit(); + int em = em_unit(); msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); + for (auto btn : { m_save_btn, m_move_btn, m_continue_btn } ) + btn->msw_rescale(); const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); + m_tree->GetColumn(UnsavedChangesModel::colToggle )->SetWidth(6 * em); + m_tree->GetColumn(UnsavedChangesModel::colIconText)->SetWidth(30 * em); + m_tree->GetColumn(UnsavedChangesModel::colOldValue)->SetWidth(20 * em); + m_tree->GetColumn(UnsavedChangesModel::colNewValue)->SetWidth(20 * em); + + m_tree_model->Rescale(); + m_tree->Refresh(); + Fit(); Refresh(); } void UnsavedChangesDialog::on_sys_color_changed() { + for (auto btn : { m_save_btn, m_move_btn, m_continue_btn } ) + btn->msw_rescale(); // msw_rescale updates just icons, so use it -// m_tree_model->msw_rescale(); + m_tree_model->Rescale(); + m_tree->Refresh(); Refresh(); } @@ -871,14 +923,14 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString auto add_header = [grid_sizer, border, this](wxString label) { wxStaticText* text = new wxStaticText(this, wxID_ANY, label); - text->SetFont(wxGetApp().bold_font()); + text->SetFont(this->GetFont().Bold()); grid_sizer->Add(text, 0, wxALL, border); }; auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); + text->SetFont(this->GetFont()); if (is_colored) -// text->SetForegroundColour(wxColour(orange)); text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 991c89442..49a9640e8 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -30,13 +30,14 @@ WX_DEFINE_ARRAY_PTR(ModelNode*, ModelNodePtrArray); // GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) class ModelNode { - wxWindow* m_parent_win{ nullptr }; + wxWindow* m_parent_win{ nullptr }; ModelNode* m_parent; ModelNodePtrArray m_children; wxBitmap m_empty_bmp; Preset::Type m_preset_type {Preset::TYPE_INVALID}; + std::string m_icon_name; // saved values for colors if they exist wxString m_old_color; wxString m_new_color; @@ -75,7 +76,7 @@ public: wxString m_new_value; // preset(root) node - ModelNode(Preset::Type preset_type, const wxString& text); + ModelNode(Preset::Type preset_type, const wxString& text, wxWindow* parent_win); // category node ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name); @@ -111,6 +112,7 @@ public: void Append(ModelNode* child) { m_children.Add(child); } void UpdateEnabling(); + void UpdateIcons(); }; @@ -120,7 +122,7 @@ public: class UnsavedChangesModel : public wxDataViewModel { - wxWindow* m_parent_win {nullptr}; + wxWindow* m_parent_win { nullptr }; std::vector m_preset_nodes; wxDataViewCtrl* m_ctrl{ nullptr }; @@ -164,6 +166,7 @@ public: unsigned int GetColumnCount() const override { return colMax; } wxString GetColumnType(unsigned int col) const override; + void Rescale(); wxDataViewItem GetParent(const wxDataViewItem& item) const override; unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; From a81e3ee2245a7c5a0c145a5e5cb3e7e72bfe690f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 12 Aug 2020 17:33:22 +0200 Subject: [PATCH 17/25] UnsavedChangesDialog : implemented "Save" function --- src/slic3r/GUI/Tab.cpp | 41 ++++++++++++++++++++----- src/slic3r/GUI/UnsavedChangesDialog.cpp | 14 +++++++-- src/slic3r/GUI/UnsavedChangesDialog.hpp | 1 + 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6501fba27..19f3974f7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -326,7 +326,7 @@ void Tab::add_scaled_button(wxWindow* parent, const wxString& label/* = wxEmptyString*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style, true); m_scaled_buttons.push_back(*btn); } @@ -3132,19 +3132,43 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) { + if (presets == nullptr) presets = m_presets; + UnsavedChangesDialog dlg(m_type, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; if (dlg.just_continue()) return true; - if (dlg.save_preset()) - // save selected changes - return false; + if (dlg.save_preset()) // save selected changes + { + std::vector unselected_options = dlg.get_unselected_options(); + const Preset& preset = presets->get_edited_preset(); + std::string name = preset.name; + + // for system/default/external presets we should take an edited name + if (preset.is_system || preset.is_default || preset.is_external) { + SavePresetDialog save_dlg(m_type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + if (save_dlg.ShowModal() != wxID_OK) + return false; + name = save_dlg.get_name(); + } + + // if we want to save just some from selected options + if (!unselected_options.empty()) + { + DynamicPrintConfig& old_config = presets->get_selected_preset().config; + + for (const std::string& opt_key : unselected_options) + m_config->set_key_value(opt_key, old_config.option(opt_key)->clone()); + } + + save_preset(name); + return true; + } if (dlg.move_preset()) // move selected changes return false; - - if (presets == nullptr) presets = m_presets; +/* // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); std::string type_name = presets->name(); @@ -3157,13 +3181,13 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr wxString changes; for (const std::string &opt_key : presets->current_dirty_options()) { const ConfigOptionDef &opt = m_config->def()->options.at(opt_key); - /*std::string*/wxString name = ""; + wxString name = ""; if (! opt.category.empty()) name += _(opt.category) + " > "; name += !opt.full_label.empty() ? _(opt.full_label) : _(opt.label); - changes += tab + /*from_u8*/(name) + "\n"; + changes += tab + (name) + "\n"; } // Show a confirmation dialog with the list of dirty options. wxString message = name + "\n\n"; @@ -3180,6 +3204,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); return confirm.ShowModal() == wxID_YES; + */ } // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 53dcf3f02..f93aa35c2 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -854,6 +854,16 @@ void UnsavedChangesDialog::update(Preset::Type type) } } +std::vector UnsavedChangesDialog::get_unselected_options() +{ + std::vector ret; + + for (auto item : m_items_map) + if (!m_tree_model->IsEnabledItem(item.first)) + ret.emplace_back(item.second.opt_key); + + return ret; +} std::vector UnsavedChangesDialog::get_selected_options() { @@ -929,9 +939,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString auto add_value = [grid_sizer, border, this](wxString label, bool is_colored = false) { wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(300, -1), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE | wxTE_RICH); - text->SetFont(this->GetFont()); - if (is_colored) - text->SetStyle(0, label.Len(), wxTextAttr(wxColour(orange))); + text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont())); grid_sizer->Add(text, 1, wxALL | wxEXPAND, border); }; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 49a9640e8..271d7595b 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -241,6 +241,7 @@ public: bool move_preset() const { return m_exit_action == Action::Move; } bool just_continue() const { return m_exit_action == Action::Continue; } + std::vector get_unselected_options(); std::vector get_selected_options(); protected: From 491e7b16f95a242510c31015c8dfb09257bfa98b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Aug 2020 08:20:22 +0200 Subject: [PATCH 18/25] Fixed build under GTK --- src/slic3r/GUI/Tab.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 19f3974f7..5c54e9e46 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3168,6 +3168,8 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (dlg.move_preset()) // move selected changes return false; + + return false; /* // Display a dialog showing the dirty options in a human readable form. const Preset& old_preset = presets->get_edited_preset(); From d7176c64bdb22061e6b603dee7042af63c628967 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 13 Aug 2020 15:45:16 +0200 Subject: [PATCH 19/25] Unsaved Changes : implemented "move" and improved "save" UnsavedChangesDialog : some code refactoring SavePresetDialog : processed empty name for the preset --- src/slic3r/GUI/PresetComboBoxes.cpp | 7 +++ src/slic3r/GUI/Tab.cpp | 80 ++++++++++-------------- src/slic3r/GUI/Tab.hpp | 1 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 81 ++++++++++++++----------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 5 +- 5 files changed, 90 insertions(+), 84 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 7539f3616..7a9ea582a 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1123,10 +1123,12 @@ void SavePresetDialog::Item::update() info_line = _L("Cannot overwrite a system profile."); m_valid_type = NoValid; } + if (m_valid_type == Valid && existing && (existing->is_external)) { info_line = _L("Cannot overwrite an external profile."); m_valid_type = NoValid; } + if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) { info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()) + "\n" + @@ -1134,6 +1136,11 @@ void SavePresetDialog::Item::update() m_valid_type = Warning; } + if (m_valid_type == Valid && m_preset_name.empty()) { + info_line = _L("The empty name is not available."); + m_valid_type = NoValid; + } + m_valid_label->SetLabel(info_line); m_valid_label->Show(!info_line.IsEmpty()); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5c54e9e46..ef124e0e6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3124,6 +3124,12 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + // check if there is something in the cache to move to the new selected preset + if (!m_cache_config.empty()) { + m_presets->get_edited_preset().config.apply(m_cache_config); + m_cache_config.clear(); + } + load_current_preset(); } } @@ -3134,11 +3140,10 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr { if (presets == nullptr) presets = m_presets; - UnsavedChangesDialog dlg(m_type, new_printer_name); + UnsavedChangesDialog dlg(m_type, presets, new_printer_name); if (dlg.ShowModal() == wxID_CANCEL) return false; - if (dlg.just_continue()) - return true; + if (dlg.save_preset()) // save selected changes { std::vector unselected_options = dlg.get_unselected_options(); @@ -3147,7 +3152,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(m_type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + SavePresetDialog save_dlg(preset.type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); if (save_dlg.ShowModal() != wxID_OK) return false; name = save_dlg.get_name(); @@ -3157,56 +3162,35 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (!unselected_options.empty()) { DynamicPrintConfig& old_config = presets->get_selected_preset().config; - + // revert unselected options to the old values for (const std::string& opt_key : unselected_options) - m_config->set_key_value(opt_key, old_config.option(opt_key)->clone()); + presets->get_edited_preset().config.set_key_value(opt_key, old_config.option(opt_key)->clone()); } - - save_preset(name); - return true; - } - if (dlg.move_preset()) - // move selected changes - return false; - return false; -/* - // Display a dialog showing the dirty options in a human readable form. - const Preset& old_preset = presets->get_edited_preset(); - std::string type_name = presets->name(); - wxString tab = " "; - wxString name = old_preset.is_default ? - from_u8((boost::format(_utf8(L("Default preset (%s)"))) % _utf8(type_name)).str()) : - from_u8((boost::format(_utf8(L("Preset (%s)"))) % _utf8(type_name)).str()) + "\n" + tab + old_preset.name; + if (m_type == presets->type()) // save changes for the current preset + save_preset(name); + else // save changes for dependent preset + { + // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini + presets->save_current_preset(name); + // Mark the print & filament enabled if they are compatible with the currently selected preset. + // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. + m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); - // Collect descriptions of the dirty options. - wxString changes; - for (const std::string &opt_key : presets->current_dirty_options()) { - const ConfigOptionDef &opt = m_config->def()->options.at(opt_key); - wxString name = ""; - if (! opt.category.empty()) - name += _(opt.category) + " > "; - name += !opt.full_label.empty() ? - _(opt.full_label) : - _(opt.label); - changes += tab + (name) + "\n"; + /* If filament preset is saved for multi-material printer preset, + * there are cases when filament comboboxs are updated for old (non-modified) colors, + * but in full_config a filament_colors option aren't.*/ + if (presets->type() == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) + wxGetApp().plater()->force_filament_colors_update(); + } } - // Show a confirmation dialog with the list of dirty options. - wxString message = name + "\n\n"; - if (new_printer_name.empty()) - message += _(L("has the following unsaved changes:")); - else { - message += (m_type == Slic3r::Preset::TYPE_PRINTER) ? - _(L("is not compatible with printer")) : - _(L("is not compatible with print profile")); - message += wxString("\n") + tab + from_u8(new_printer_name) + "\n\n"; - message += _(L("and it has the following unsaved changes:")); + else if (dlg.move_preset()) // move selected changes + { + // copy selected options to the cache from edited preset + m_cache_config.apply_only(*m_config, dlg.get_selected_options()); } - wxMessageDialog confirm(parent(), - message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), - _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return confirm.ShowModal() == wxID_YES; - */ + + return true; } // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 24f25e2d7..c9639bd00 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -236,6 +236,7 @@ public: bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; + DynamicPrintConfig m_cache_config; ogStaticText* m_parent_preset_description_line; ScalableButton* m_detach_preset_btn = nullptr; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index f93aa35c2..67ccc6b6a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -514,7 +514,7 @@ void UnsavedChangesModel::Rescale() // UnsavedChangesDialog //------------------------------------------ -UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset) +UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -530,6 +530,9 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& int border = 10; int em = em_unit(); + m_action_line = new wxStaticText(this, wxID_ANY, ""); + m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_tree = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(em * 80, em * 30), wxBORDER_SIMPLE | wxDV_VARIABLE_LINE_HEIGHT | wxDV_ROW_LINES); m_tree_model = new UnsavedChangesModel(this); m_tree->AssociateModel(m_tree_model); @@ -561,36 +564,24 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& m_tree->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &UnsavedChangesDialog::context_menu, this); // Add Buttons - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCANCEL); - Tab* tab = wxGetApp().get_tab(type); - assert(tab); - PresetCollection* presets = tab->get_presets(); + auto add_btn = [this, buttons](ScalableButton** btn, int& btn_id, const std::string& icon_name, Action close_act, int idx, bool process_enable = true) + { + *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, "", wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); + buttons->Insert(idx, *btn, 0, wxLEFT, 5); - wxString label= from_u8((boost::format(_u8L("Save selected to preset: %1%"))% ("\"" + presets->get_selected_preset().name + "\"")).str()); - m_save_btn = new ScalableButton(this, m_save_btn_id = NewControlId(), "save", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(0, m_save_btn, 0, wxLEFT, 5); + (*btn)->Bind(wxEVT_BUTTON, [this, close_act](wxEvent&) { close(close_act); }); + if (process_enable) + (*btn)->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); + (*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); + }; - m_save_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Save); }); - m_save_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_save_btn->Bind(wxEVT_ENTER_WINDOW,[this, presets](wxMouseEvent& e){ show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); - m_save_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); - - label = from_u8((boost::format(_u8L("Move selected to preset: %1%"))% ("\"" + from_u8(new_selected_preset) + "\"")).str()); - m_move_btn = new ScalableButton(this, m_move_btn_id = NewControlId(), "paste_menu", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(1, m_move_btn, 0, wxLEFT, 5); - - m_move_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Move); }); - m_move_btn->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(!m_empty_selection); }); - m_move_btn->Bind(wxEVT_ENTER_WINDOW,[this, new_selected_preset](wxMouseEvent& e){ show_info_line(Action::Move, new_selected_preset); e.Skip(); }); - m_move_btn->Bind(wxEVT_LEAVE_WINDOW,[this ](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); - - label = _L("Continue without changes"); - m_continue_btn = new ScalableButton(this, m_continue_btn_id = NewControlId(), "cross", label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true); - buttons->Insert(2, m_continue_btn, 0, wxLEFT, 5); - m_continue_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { close(Action::Continue); }); - m_continue_btn->Bind(wxEVT_ENTER_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Continue); e.Skip(); }); - m_continue_btn->Bind(wxEVT_LEAVE_WINDOW,[this](wxMouseEvent& e){ show_info_line(Action::Undef); e.Skip(); }); + int btn_idx = 0; + add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); + if (type == dependent_presets->type()) + add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); + add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); m_info_line = new wxStaticText(this, wxID_ANY, ""); m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); @@ -598,12 +589,12 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, const std::string& wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - topSizer->Add(new wxStaticText(this, wxID_ANY, _L("There are unsaved changes for") + (": \"" + tab->title() + "\"")), 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_action_line,0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_tree, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); - update(type); + update(type, dependent_presets, new_selected_preset); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -824,12 +815,34 @@ wxString UnsavedChangesDialog::get_short_string(wxString full_string) return full_string + dots; } -void UnsavedChangesDialog::update(Preset::Type type) +void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) { - Tab* tab = wxGetApp().get_tab(type); - assert(tab); + PresetCollection* presets = dependent_presets; + + // activate buttons and labels + m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + if (m_move_btn) + m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); + + m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); + m_continue_btn->SetLabel(_L("Continue without changes")); + + wxString action_msg; + if (type == dependent_presets->type()) { + action_msg = _L("has the following unsaved changes:"); + + m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + } + else { + action_msg = type == Preset::TYPE_PRINTER ? + _L("is not compatible with printer") : + _L("is not compatible with print profile"); + action_msg += " \"" + from_u8(new_selected_preset) + "\" "; + action_msg += _L("and it has the following unsaved changes:"); + } + m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); - PresetCollection* presets = tab->get_presets(); // Display a dialog showing the dirty options in a human readable form. const DynamicPrintConfig& old_config = presets->get_selected_preset().config; const DynamicPrintConfig& new_config = presets->get_edited_preset().config; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 271d7595b..afb73a4f9 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -193,6 +193,7 @@ class UnsavedChangesDialog : public DPIDialog ScalableButton* m_save_btn { nullptr }; ScalableButton* m_move_btn { nullptr }; ScalableButton* m_continue_btn { nullptr }; + wxStaticText* m_action_line { nullptr }; wxStaticText* m_info_line { nullptr }; bool m_empty_selection { false }; @@ -226,12 +227,12 @@ class UnsavedChangesDialog : public DPIDialog std::map m_items_map; public: - UnsavedChangesDialog(Preset::Type type, const std::string& new_selected_preset); + UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} wxString get_short_string(wxString full_string); - void update(Preset::Type type); + void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); void item_value_changed(wxDataViewEvent &event); void context_menu(wxDataViewEvent &event); void show_info_line(Action action, std::string preset_name = ""); From 618f04717fcd2a62978685f3058ee4bb23815c09 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 14 Aug 2020 18:17:16 +0200 Subject: [PATCH 20/25] Unsaved Changes : improvement for the GUI_App::check_unsaved_changes() Added use of UnsavedChangesDialog --- src/libslic3r/PresetBundle.cpp | 26 ++++++ src/libslic3r/PresetBundle.hpp | 4 + src/slic3r/GUI/GUI_App.cpp | 77 ++++++++++++---- src/slic3r/GUI/PresetComboBoxes.cpp | 54 ++++++++---- src/slic3r/GUI/PresetComboBoxes.hpp | 8 +- src/slic3r/GUI/Tab.cpp | 27 ++---- src/slic3r/GUI/UnsavedChangesDialog.cpp | 112 +++++++++++++++++------- src/slic3r/GUI/UnsavedChangesDialog.hpp | 18 ++-- 8 files changed, 228 insertions(+), 98 deletions(-) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 108985704..ac1b0a717 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -327,6 +327,32 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p return presets.get_preset_name_by_alias(alias); } +void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type, + const std::vector& unselected_options) +{ + PresetCollection& presets = type == Preset::TYPE_PRINT ? prints : + type == Preset::TYPE_SLA_PRINT ? sla_prints : + type == Preset::TYPE_FILAMENT ? filaments : + type == Preset::TYPE_SLA_MATERIAL ? sla_materials : printers; + + // if we want to save just some from selected options + if (!unselected_options.empty()) { + // revert unselected options to the old values + presets.get_edited_preset().config.apply_only(presets.get_selected_preset().config, unselected_options); + } + + // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini + presets.save_current_preset(new_name); + // Mark the print & filament enabled if they are compatible with the currently selected preset. + // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. + update_compatible(PresetSelectCompatibleType::Never); + + if (type == Preset::TYPE_FILAMENT) { + // synchronize the first filament presets. + set_filament_preset(0, filaments.get_selected_preset_name()); + } +} + void PresetBundle::load_installed_filaments(AppConfig &config) { if (! config.has_section(AppConfig::SECTION_FILAMENTS)) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 567a12331..ff02bbeae 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -130,6 +130,10 @@ public: const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const; + // Save current preset of a required type under a new name. If the name is different from the old one, + // Unselected option would be reverted to the beginning values + void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector& unselected_options); + static const char *PRUSA_BUNDLE; private: std::string load_system_presets(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 82c2861bc..266fd8fd6 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -57,6 +57,8 @@ #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" #include "NotificationManager.hpp" +#include "UnsavedChangesDialog.hpp" +#include "PresetComboBoxes.hpp" #ifdef __WXMSW__ #include @@ -1157,29 +1159,66 @@ void GUI_App::add_config_menu(wxMenuBar *menu) // to notify the user whether he is aware that some preset changes will be lost. bool GUI_App::check_unsaved_changes(const wxString &header) { - wxString dirty; PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); - for (Tab *tab : tabs_list) + + bool has_unsaved_changes = false; + for (Tab* tab : tabs_list) if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) { - if (dirty.empty()) - dirty = tab->title(); - else - dirty += wxString(", ") + tab->title(); + has_unsaved_changes = true; + break; } - if (dirty.empty()) - // No changes, the application may close or reload presets. - return true; - // Ask the user. - wxString message; - if (! header.empty()) - message = header + "\n\n"; - message += _(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?")); - wxMessageDialog dialog(mainframe, - message, - wxString(SLIC3R_APP_NAME) + " - " + _(L("Unsaved Presets")), - wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return dialog.ShowModal() == wxID_YES; + if (has_unsaved_changes) + { + UnsavedChangesDialog dlg(header); + if (dlg.ShowModal() == wxID_CANCEL) + return false; + + if (dlg.save_preset()) // save selected changes + { + struct NameType + { + std::string name; + Preset::Type type {Preset::TYPE_INVALID}; + }; + + std::vector names_and_types; + + // for system/default/external presets we should take an edited name + std::vector types; + for (Tab* tab : tabs_list) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + { + const Preset& preset = tab->get_presets()->get_edited_preset(); + if (preset.is_system || preset.is_default || preset.is_external) + types.emplace_back(preset.type); + + names_and_types.emplace_back(NameType{ preset.name, preset.type }); + } + + + if (!types.empty()) { + SavePresetDialog save_dlg(types); + if (save_dlg.ShowModal() != wxID_OK) + return false; + + for (NameType& nt : names_and_types) { + const std::string name = save_dlg.get_name(nt.type); + if (!name.empty()) + nt.name = name; + } + } + + for (const NameType& nt : names_and_types) + preset_bundle->save_changes_for_preset(nt.name, nt.type, dlg.get_unselected_options(nt.type)); + + // if we saved changes to the new presets, we should to + // synchronize config.ini with the current selections. + preset_bundle->export_selections(*app_config); + } + } + + return true; } bool GUI_App::checked_tab(Tab* tab) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 87db32ac6..9b0c9d0c8 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1170,8 +1170,26 @@ void SavePresetDialog::Item::accept() // SavePresetDialog //----------------------------------------------- -SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) +SavePresetDialog::SavePresetDialog(Preset::Type type, std::string suffix) : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) +{ + build(std::vector{type}, suffix); +} + +SavePresetDialog::SavePresetDialog(std::vector types, std::string suffix) + : DPIDialog(nullptr, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) +{ + build(types, suffix); +} + +SavePresetDialog::~SavePresetDialog() +{ + for (auto item : m_items) { + delete item; + } +} + +void SavePresetDialog::build(std::vector types, std::string suffix) { SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT @@ -1179,14 +1197,18 @@ SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); -#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT +#endif // ENABLE_WX_3_1_3_DPI_CHANGED_EVENT + + if (suffix.empty()) + suffix = _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); m_presets_sizer = new wxBoxSizer(wxVERTICAL); // Add first item - m_items.emplace_back(type, suffix, m_presets_sizer, this); + for (Preset::Type type : types) + AddItem(type, suffix); // Add dialog's buttons wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); @@ -1203,26 +1225,26 @@ SavePresetDialog::SavePresetDialog(Preset::Type type, const std::string& suffix) void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix) { - m_items.emplace_back(type, suffix, m_presets_sizer, this); + m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this}); } std::string SavePresetDialog::get_name() { - return m_items.front().preset_name(); + return m_items.front()->preset_name(); } std::string SavePresetDialog::get_name(Preset::Type type) { - for (Item& item : m_items) - if (item.type() == type) - return item.preset_name(); + for (const Item* item : m_items) + if (item->type() == type) + return item->preset_name(); return ""; } bool SavePresetDialog::enable_ok_btn() const { - for (Item item : m_items) - if (!item.is_valid()) + for (const Item* item : m_items) + if (!item->is_valid()) return false; return true; @@ -1291,8 +1313,8 @@ void SavePresetDialog::on_dpi_changed(const wxRect& suggested_rect) msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - for (Item& item : m_items) - item.update_valid_bmp(); + for (Item* item : m_items) + item->update_valid_bmp(); //const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(/*size*/wxSize(100, 50)); @@ -1331,10 +1353,10 @@ void SavePresetDialog::update_physical_printers(const std::string& preset_name) void SavePresetDialog::accept() { - for (Item& item : m_items) { - item.accept(); - if (item.type() == Preset::TYPE_PRINTER) - update_physical_printers(item.preset_name()); + for (Item* item : m_items) { + item->accept(); + if (item->type() == Preset::TYPE_PRINTER) + update_physical_printers(item->preset_name()); } EndModal(wxID_OK); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 7f51f775e..e33a2d753 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -235,7 +235,7 @@ class SavePresetDialog : public DPIDialog void update(); }; - std::vector m_items; + std::vector m_items; wxBoxSizer* m_presets_sizer {nullptr}; wxStaticText* m_label {nullptr}; @@ -248,8 +248,9 @@ class SavePresetDialog : public DPIDialog public: - SavePresetDialog(Preset::Type type, const std::string& suffix); - ~SavePresetDialog() {} + SavePresetDialog(Preset::Type type, std::string suffix = ""); + SavePresetDialog(std::vector types, std::string suffix = ""); + ~SavePresetDialog(); void AddItem(Preset::Type type, const std::string& suffix); @@ -266,6 +267,7 @@ protected: void on_sys_color_changed() override {} private: + void build(std::vector types, std::string suffix = ""); void update_physical_printers(const std::string& preset_name); void accept(); }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index cf999a409..1363ddcad 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3146,36 +3146,27 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr if (dlg.save_preset()) // save selected changes { - std::vector unselected_options = dlg.get_unselected_options(); + std::vector unselected_options = dlg.get_unselected_options(presets->type()); const Preset& preset = presets->get_edited_preset(); std::string name = preset.name; // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(preset.type, _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName")); + SavePresetDialog save_dlg(preset.type); if (save_dlg.ShowModal() != wxID_OK) return false; name = save_dlg.get_name(); } - // if we want to save just some from selected options - if (!unselected_options.empty()) + if (m_type == presets->type()) // save changes for the current preset from this tab { - DynamicPrintConfig& old_config = presets->get_selected_preset().config; // revert unselected options to the old values - for (const std::string& opt_key : unselected_options) - presets->get_edited_preset().config.set_key_value(opt_key, old_config.option(opt_key)->clone()); - } - - if (m_type == presets->type()) // save changes for the current preset + presets->get_edited_preset().config.apply_only(presets->get_selected_preset().config, unselected_options); save_preset(name); - else // save changes for dependent preset + } + else { - // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini - presets->save_current_preset(name); - // Mark the print & filament enabled if they are compatible with the currently selected preset. - // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. - m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + m_preset_bundle->save_changes_for_preset(name, presets->type(), unselected_options); /* If filament preset is saved for multi-material printer preset, * there are cases when filament comboboxs are updated for old (non-modified) colors, @@ -3283,10 +3274,8 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // focus currently.is there anything better than this ? //! m_treectrl->OnSetFocus(); - std::string suffix = detach ? _utf8(L("Detached")) : _CTX_utf8(L_CONTEXT("Copy", "PresetName"), "PresetName"); - if (name.empty()) { - SavePresetDialog dlg(m_type, suffix); + SavePresetDialog dlg(m_type, detach ? _u8L("Detached") : ""); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 67ccc6b6a..2fa89266e 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -514,8 +514,19 @@ void UnsavedChangesModel::Rescale() // UnsavedChangesDialog //------------------------------------------ +UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header) + : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + build(Preset::TYPE_INVALID, nullptr, "", header); +} + UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) : DPIDialog(nullptr, wxID_ANY, _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + build(type, dependent_presets, new_selected_preset); +} + +void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header) { wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); SetBackgroundColour(bgr_clr); @@ -579,7 +590,8 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* int btn_idx = 0; add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); - if (type == dependent_presets->type()) + if (type != Preset::TYPE_INVALID && type == dependent_presets->type() && + dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology()) add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); @@ -594,7 +606,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection* topSizer->Add(m_info_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 2*border); topSizer->Add(buttons, 0, wxEXPAND | wxALL, border); - update(type, dependent_presets, new_selected_preset); + update(type, dependent_presets, new_selected_preset, header); SetSizer(topSizer); topSizer->SetSizeHints(this); @@ -654,8 +666,12 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name else if (action == Action::Continue) text = _L("All changed options will be reverted."); else { - std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); - text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + if (action == Action::Save && preset_name.empty()) + text = _L("After press this button selected options will be saved"); + else { + std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + } text += "\n" + _L("Unselected options will be reverted."); } m_info_line->SetLabel(text); @@ -815,64 +831,92 @@ wxString UnsavedChangesDialog::get_short_string(wxString full_string) return full_string + dots; } -void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset) +void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header) { PresetCollection* presets = dependent_presets; // activate buttons and labels - m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets->get_selected_preset().name); e.Skip(); }); + m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); if (m_move_btn) m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); - m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); m_continue_btn->SetLabel(_L("Continue without changes")); - wxString action_msg; - if (type == dependent_presets->type()) { - action_msg = _L("has the following unsaved changes:"); - - m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + if (type == Preset::TYPE_INVALID) { + m_action_line ->SetLabel(header + "\n" + _L("Next presets have the following unsaved changes:")); + m_save_btn ->SetLabel(_L("Save selected")); } else { - action_msg = type == Preset::TYPE_PRINTER ? - _L("is not compatible with printer") : - _L("is not compatible with print profile"); - action_msg += " \"" + from_u8(new_selected_preset) + "\" "; - action_msg += _L("and it has the following unsaved changes:"); + wxString action_msg; + if (type == dependent_presets->type()) { + action_msg = _L("has the following unsaved changes:"); + if (m_move_btn) + m_move_btn->SetLabel(from_u8((boost::format(_u8L("Move selected to preset: %1%")) % ("\"" + new_selected_preset + "\"")).str())); + } + else { + action_msg = type == Preset::TYPE_PRINTER ? + _L("is not compatible with printer") : + _L("is not compatible with print profile"); + action_msg += " \"" + from_u8(new_selected_preset) + "\"\n"; + action_msg += _L("and it has the following unsaved changes:"); + } + m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); + m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str())); } - m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); - // Display a dialog showing the dirty options in a human readable form. - const DynamicPrintConfig& old_config = presets->get_selected_preset().config; - const DynamicPrintConfig& new_config = presets->get_edited_preset().config; - - m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + update_tree(type, presets); +} +void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_) +{ Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); searcher.sort_options_by_opt_key(); - // Collect dirty options. - for (const std::string& opt_key : presets->current_dirty_options()) { - const Search::Option& option = searcher.get_option(opt_key); + // list of the presets with unsaved changes + std::vector presets_list; + if (type == Preset::TYPE_INVALID) + { + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); - ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config) }; + for (Tab* tab : wxGetApp().tabs_list) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + presets_list.emplace_back(tab->get_presets()); + } + else + presets_list.emplace_back(presets_); - wxString old_val = get_short_string(item_data.old_val); - wxString new_val = get_short_string(item_data.new_val); - if (old_val != item_data.old_val || new_val != item_data.new_val) - item_data.is_long = true; + // Display a dialog showing the dirty options in a human readable form. + for (PresetCollection* presets : presets_list) + { + const DynamicPrintConfig& old_config = presets->get_selected_preset().config; + const DynamicPrintConfig& new_config = presets->get_edited_preset().config; + type = presets->type(); - m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); + m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); + + // Collect dirty options. + for (const std::string& opt_key : presets->current_dirty_options()) { + const Search::Option& option = searcher.get_option(opt_key); + + ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type }; + + wxString old_val = get_short_string(item_data.old_val); + wxString new_val = get_short_string(item_data.new_val); + if (old_val != item_data.old_val || new_val != item_data.new_val) + item_data.is_long = true; + + m_items_map.emplace(m_tree_model->AddOption(type, option.category_local, option.group_local, option.label_local, old_val, new_val), item_data); + } } } -std::vector UnsavedChangesDialog::get_unselected_options() +std::vector UnsavedChangesDialog::get_unselected_options(Preset::Type type) { std::vector ret; for (auto item : m_items_map) - if (!m_tree_model->IsEnabledItem(item.first)) + if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first)) ret.emplace_back(item.second.opt_key); return ret; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index afb73a4f9..f78a1fec0 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -217,22 +217,26 @@ class UnsavedChangesDialog : public DPIDialog struct ItemData { - std::string opt_key; - wxString opt_name; - wxString old_val; - wxString new_val; - bool is_long {false}; + std::string opt_key; + wxString opt_name; + wxString old_val; + wxString new_val; + Preset::Type type; + bool is_long {false}; }; // tree items related to the options std::map m_items_map; public: + UnsavedChangesDialog(const wxString& header); UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); ~UnsavedChangesDialog() {} wxString get_short_string(wxString full_string); - void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset); + void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = ""); + void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header); + void update_tree(Preset::Type type, PresetCollection *presets); void item_value_changed(wxDataViewEvent &event); void context_menu(wxDataViewEvent &event); void show_info_line(Action action, std::string preset_name = ""); @@ -242,7 +246,7 @@ public: bool move_preset() const { return m_exit_action == Action::Move; } bool just_continue() const { return m_exit_action == Action::Continue; } - std::vector get_unselected_options(); + std::vector get_unselected_options(Preset::Type type); std::vector get_selected_options(); protected: From e050fb68bf3e486f46a41d0e6816c912300cac08 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Aug 2020 15:13:18 +0200 Subject: [PATCH 21/25] Fixed UI changing update for "Ramming" parameter --- src/slic3r/GUI/OptionsGroup.cpp | 7 ++++++- src/slic3r/GUI/Tab.cpp | 33 ++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 1bebb8827..cc10d815f 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -466,7 +466,8 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, } else if (m_opt_map.find(opt_key) == m_opt_map.end() || // This option don't have corresponded field - opt_key == "bed_shape" || opt_key == "compatible_printers" || opt_key == "compatible_prints" ) { + opt_key == "bed_shape" || opt_key == "filament_ramming_parameters" || + opt_key == "compatible_printers" || opt_key == "compatible_prints" ) { value = get_config_value(config, opt_key); change_opt_value(*m_config, opt_key, value); return; @@ -699,6 +700,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = config.option(opt_key)->values; break; } + if (opt_key == "filament_ramming_parameters") { + ret = config.opt_string(opt_key, static_cast(idx)); + break; + } if (config.option(opt_key)->values.empty()) ret = text_value; else if (opt->gui_flags.compare("serialized") == 0) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1363ddcad..a16222c86 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -413,8 +413,9 @@ void Tab::update_labels_colour() else color = &m_modified_label_clr; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { - wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers" ) { + wxStaticText* label = m_colored_Labels.find(opt.first) == m_colored_Labels.end() ? nullptr : m_colored_Labels.at(opt.first); if (label) { label->SetForegroundColour(*color); label->Refresh(true); @@ -504,7 +505,8 @@ void Tab::update_changed_ui() icon = &m_bmp_white_bullet; tt = &m_tt_white_bullet; } - if (opt.first == "bed_shape" || opt.first == "compatible_prints" || opt.first == "compatible_printers") { + if (opt.first == "bed_shape" || opt.first == "filament_ramming_parameters" || + opt.first == "compatible_prints" || opt.first == "compatible_printers") { wxStaticText* label = (m_colored_Labels.find(opt.first) == m_colored_Labels.end()) ? nullptr : m_colored_Labels.at(opt.first); if (label) { label->SetForegroundColour(*color); @@ -656,6 +658,9 @@ void Tab::update_changed_tree_ui() get_sys_and_mod_flags(opt_key, sys_page, modified_page); } } + if (m_type == Preset::TYPE_FILAMENT && page->title() == "Advanced") { + get_sys_and_mod_flags("filament_ramming_parameters", sys_page, modified_page); + } if (page->title() == "Dependencies") { if (m_type == Slic3r::Preset::TYPE_PRINTER) { sys_page = m_presets->get_selected_preset_parent() != nullptr; @@ -734,7 +739,10 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) 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); } - + } + if (group->title == "Toolchange parameters with single extruder MM printers") { + if ((m_options_list["filament_ramming_parameters"] & os) == 0) + to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters"); } if (group->title == "Profile dependencies") { // "compatible_printers" option doesn't exists in Printer Settimgs Tab @@ -1737,22 +1745,21 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_initial_speed"); optgroup->append_single_option_line("filament_cooling_final_speed"); - line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; - line.widget = [this](wxWindow* parent) { + create_line_with_widget(optgroup.get(), "filament_ramming_parameters", [this](wxWindow* parent) { auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); ramming_dialog_btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(ramming_dialog_btn); - ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + ramming_dialog_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { RammingDialog dlg(this,(m_config->option("filament_ramming_parameters"))->get_at(0)); - if (dlg.ShowModal() == wxID_OK) - (m_config->option("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters(); - })); + if (dlg.ShowModal() == wxID_OK) { + load_key_value("filament_ramming_parameters", dlg.get_parameters()); + update_changed_ui(); + } + }); return sizer; - }; - optgroup->append_line(line); + }); add_filament_overrides_page(); From 4641d44544389324df278fbe5f062aa6db58382e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Aug 2020 16:49:06 +0200 Subject: [PATCH 22/25] UnsavedChangesDialog : improvements * Processed changes in options with nullable values * Processed changes on the extruders count --- src/slic3r/GUI/Tab.cpp | 37 +++++++++- src/slic3r/GUI/Tab.hpp | 3 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 93 ++++++++++++++++++------- 3 files changed, 106 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a16222c86..898890f6e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2866,7 +2866,7 @@ void Tab::load_current_preset() } on_presets_changed(); if (printer_technology == ptFFF) { - static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; + static_cast(this)->m_initial_extruders_count = static_cast(m_presets->get_selected_preset().config.option("nozzle_diameter"))->values.size(); //static_cast(this)->m_extruders_count; const Preset* parent_preset = m_presets->get_selected_preset_parent(); static_cast(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : static_cast(parent_preset->config.option("nozzle_diameter"))->values.size(); @@ -3131,6 +3131,10 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } + // check and apply extruders count for printer preset + if (m_type == Preset::TYPE_PRINTER) + static_cast(this)->apply_extruder_cnt_from_cache(); + // check if there is something in the cache to move to the new selected preset if (!m_cache_config.empty()) { m_presets->get_edited_preset().config.apply(m_cache_config); @@ -3184,8 +3188,18 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr } else if (dlg.move_preset()) // move selected changes { + std::vector selected_options = dlg.get_selected_options(); + auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); + if (it != selected_options.end()) { + // erase "extruders_count" option from the list + selected_options.erase(it); + // cache the extruders count + if (m_type == Preset::TYPE_PRINTER) + static_cast(this)->cache_extruder_cnt(); + } + // copy selected options to the cache from edited preset - m_cache_config.apply_only(*m_config, dlg.get_selected_options()); + m_cache_config.apply_only(*m_config, selected_options); } return true; @@ -3593,6 +3607,25 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) return sizer; } +void TabPrinter::cache_extruder_cnt() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + m_cache_extruder_count = m_extruders_count; +} + +void TabPrinter::apply_extruder_cnt_from_cache() +{ + if (m_presets->get_edited_preset().printer_technology() == ptSLA) + return; + + if (m_cache_extruder_count > 0) { + m_presets->get_edited_preset().set_num_extruders(m_cache_extruder_count); + m_cache_extruder_count = 0; + } +} + void Tab::compatible_widget_reload(PresetDependencies &deps) { bool has_any = ! m_config->option(deps.key_list)->values.empty(); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index a97153f47..9bddebeab 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -411,6 +411,7 @@ public: size_t m_extruders_count_old = 0; size_t m_initial_extruders_count; size_t m_sys_extruders_count; + size_t m_cache_extruder_count = 0; PrinterTechnology m_printer_technology = ptFFF; @@ -437,6 +438,8 @@ public: bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } wxSizer* create_bed_shape_widget(wxWindow* parent); + void cache_extruder_cnt(); + void apply_extruder_cnt_from_cache(); }; class TabSLAMaterial : public Tab diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 2fa89266e..bebc01c78 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -712,47 +712,71 @@ wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConf return from_u8(_utf8(names[static_cast(val)])); } -static wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig& config) +static int get_id_from_opt_key(std::string opt_key) { + int pos = opt_key.find("#"); + if (pos > 0) { + boost::erase_head(opt_key, pos + 1); + return atoi(opt_key.c_str()); + } + return 0; +} + +static std::string get_pure_opt_key(std::string opt_key) +{ + int pos = opt_key.find("#"); + if (pos > 0) + boost::erase_tail(opt_key, opt_key.size() - pos); + return opt_key; +} + +static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) +{ + int opt_idx = get_id_from_opt_key(opt_key); + opt_key = get_pure_opt_key(opt_key); + + if (config.option(opt_key)->is_nil()) + return _L("N/A"); + wxString out; // FIXME controll, if opt_key has index - int opt_idx = 0; - ConfigOptionType type = config.def()->options.at(opt_key).type; + const ConfigOptionDef* opt = config.def()->get(opt_key); + bool is_nullable = opt->nullable; - switch (type) { + switch (opt->type) { case coInt: return from_u8((boost::format("%1%") % config.opt_int(opt_key)).str()); case coInts: { - const ConfigOptionInts* opt = config.opt(opt_key); - if (opt) - return from_u8((boost::format("%1%") % opt->get_at(opt_idx)).str()); - break; + int val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("%1%") % val).str()); } case coBool: return config.opt_bool(opt_key) ? "true" : "false"; case coBools: { - const ConfigOptionBools* opt = config.opt(opt_key); - if (opt) - return opt->get_at(opt_idx) ? "true" : "false"; - break; + bool val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return val ? "true" : "false"; } case coPercent: return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); case coPercents: { - const ConfigOptionPercents* opt = config.opt(opt_key); - if (opt) - return from_u8((boost::format("%1%%%") % int(opt->get_at(opt_idx))).str()); - break; + double val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("%1%%%") % int(val)).str()); } case coFloat: return double_to_string(config.opt_float(opt_key)); case coFloats: { - const ConfigOptionFloats* opt = config.opt(opt_key); - if (opt) - return double_to_string(opt->get_at(opt_idx)); - break; + double val = is_nullable ? + config.opt(opt_key)->get_at(opt_idx) : + config.opt(opt_key)->get_at(opt_idx); + return double_to_string(val); } case coString: return from_u8(config.opt_string(opt_key)); @@ -896,7 +920,23 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres m_tree_model->AddPreset(type, from_u8(presets->get_edited_preset().name)); // Collect dirty options. - for (const std::string& opt_key : presets->current_dirty_options()) { + const bool deep_compare = (type == Preset::TYPE_PRINTER || type == Preset::TYPE_SLA_MATERIAL); + auto dirty_options = presets->current_dirty_options(deep_compare); + auto dirty_options_ = presets->current_dirty_options(); + + // process changes of extruders count + if (type == Preset::TYPE_PRINTER && + old_config.opt("extruder_colour")->values.size() != new_config.opt("extruder_colour")->values.size()) { + wxString local_label = _L("Extruders count"); + wxString old_val = from_u8((boost::format("%1%") % old_config.opt("extruder_colour")->values.size()).str()); + wxString new_val = from_u8((boost::format("%1%") % new_config.opt("extruder_colour")->values.size()).str()); + + ItemData item_data = { "extruders_count", local_label, old_val, new_val, type }; + m_items_map.emplace(m_tree_model->AddOption(type, _L("General"), _L("Capabilities"), local_label, old_val, new_val), item_data); + + } + + for (const std::string& opt_key : /*presets->current_dirty_options()*/dirty_options) { const Search::Option& option = searcher.get_option(opt_key); ItemData item_data = { opt_key, option.label_local, get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), type }; @@ -915,9 +955,12 @@ std::vector UnsavedChangesDialog::get_unselected_options(Preset::Ty { std::vector ret; - for (auto item : m_items_map) + for (auto item : m_items_map) { + if (item.second.opt_key == "extruders_count") + continue; if (item.second.type == type && !m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second.opt_key); + ret.emplace_back(get_pure_opt_key(item.second.opt_key)); + } return ret; } @@ -926,9 +969,9 @@ std::vector UnsavedChangesDialog::get_selected_options() { std::vector ret; - for (auto item : m_items_map) + for (auto item : m_items_map) if (m_tree_model->IsEnabledItem(item.first)) - ret.emplace_back(item.second.opt_key); + ret.emplace_back(get_pure_opt_key(item.second.opt_key)); return ret; } From 15285a68a0437c1d933d48964e655f6508c7ab5f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Aug 2020 13:04:51 +0200 Subject: [PATCH 23/25] BedShape is extracted to the separate structure --- src/slic3r/GUI/BedShapeDialog.cpp | 102 +++++++++++++++++++++--- src/slic3r/GUI/BedShapeDialog.hpp | 96 ++++++++++++++++++++++ src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 +- 3 files changed, 190 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index f08b1a379..98bd9a267 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -59,6 +59,81 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) const std::string BedShapePanel::NONE = "None"; const std::string BedShapePanel::EMPTY_STRING = ""; +static std::string get_option_label(BedShape::Parameter param) +{ + switch (param) { + case BedShape::Parameter::RectSize : return L("Size"); + case BedShape::Parameter::RectOrigin: return L("Origin"); + case BedShape::Parameter::Diameter : return L("Diameter"); + default: return ""; + } +} + +void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter param) +{ + ConfigOptionDef def; + + if (param == Parameter::RectSize) { + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); + def.min = 0; + def.max = 1200; + def.label = get_option_label(param); + def.tooltip = L("Size in X and Y of the rectangular plate."); + + Option option(def, "rect_size"); + optgroup->append_single_option_line(option); + } + else if (param == Parameter::RectOrigin) { + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); + def.min = -600; + def.max = 600; + def.label = get_option_label(param); + def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); + + Option option(def, "rect_origin"); + optgroup->append_single_option_line(option); + } + else if (param == Parameter::Diameter) { + def.type = coFloat; + def.set_default_value(new ConfigOptionFloat(200)); + def.sidetext = L("mm"); + def.label = get_option_label(param); + def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); + + Option option(def, "diameter"); + optgroup->append_single_option_line(option); + } +} + +wxString BedShape::get_name(Type type) +{ + switch (type) { + case Type::Rectangular : return _L("Rectangular"); + case Type::Circular : return _L("Circular"); + case Type::Custom : return _L("Custom"); + case Type::Invalid : + default : return _L("Invalid"); + } +} + +wxString BedShape::get_full_name_with_params() +{ + wxString out = _L("Shape") + ": " + get_name(type); + + if (type == Type::Rectangular) { + out += "\n" + get_option_label(Parameter::RectSize) + +": [" + ConfigOptionPoint(rectSize).serialize() + "]"; + out += "\n" + get_option_label(Parameter::RectOrigin) + +": [" + ConfigOptionPoint(rectOrigin).serialize() + "]"; + } + else if (type == Type::Circular) + out += "\n" + get_option_label(Parameter::Diameter) + +": [" + double_to_string(diameter) + "]"; + else if (type == Type::Custom) + out += "\n" + double_to_string(diameter); + + return out; +} + void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { m_shape = default_pt.values; @@ -72,7 +147,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); sbsizer->Add(m_shape_options_book); - auto optgroup = init_shape_options_page(_(L("Rectangular"))); +/* auto optgroup = init_shape_options_page(_(L("Rectangular"))); ConfigOptionDef def; def.type = coPoints; def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); @@ -100,22 +175,31 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); option = Option(def, "diameter"); optgroup->append_single_option_line(option); +*/ + + auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular)); + BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); + BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); + + optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular)); + BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); + +// optgroup = init_shape_options_page(_(L("Custom"))); + optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom)); - optgroup = init_shape_options_page(_(L("Custom"))); Line line{ "", "" }; line.full_width = 1; line.widget = [this](wxWindow* parent) { - wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL..."))); + wxButton* shape_btn = new wxButton(parent, wxID_ANY, _L("Load shape from STL...")); wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL); shape_sizer->Add(shape_btn, 1, wxEXPAND); wxSizer* sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(shape_sizer, 1, wxEXPAND); - shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + shape_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { load_stl(); - })); + }); return sizer; }; @@ -494,8 +578,8 @@ void BedShapePanel::load_stl() if (dialog.ShowModal() != wxID_OK) return; - std::string file_name = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(file_name, ".stl")) + m_custom_shape = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(m_custom_shape, ".stl")) { show_error(this, _(L("Invalid file format."))); return; @@ -505,7 +589,7 @@ void BedShapePanel::load_stl() Model model; try { - model = Model::read_from_file(file_name); + model = Model::read_from_file(m_custom_shape); } catch (std::exception &) { show_error(this, _(L("Error! Invalid model"))); diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index b583eca4a..9064e7ddc 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -16,6 +16,101 @@ namespace GUI { class ConfigOptionsGroup; using ConfigOptionsGroupShp = std::shared_ptr; + +struct BedShape { + + enum class Type { + Rectangular = 0, + Circular, + Custom, + Invalid + }; + + enum class Parameter { + RectSize, + RectOrigin, + Diameter + }; + + BedShape(const ConfigOptionPoints& points) { + auto polygon = Polygon::new_scale(points.values); + + // is this a rectangle ? + if (points.size() == 4) { + auto lines = polygon.lines(); + if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { + // okay, it's a rectangle + // find origin + coordf_t x_min, x_max, y_min, y_max; + x_max = x_min = points.values[0](0); + y_max = y_min = points.values[0](1); + for (auto pt : points.values) + { + x_min = std::min(x_min, pt(0)); + x_max = std::max(x_max, pt(0)); + y_min = std::min(y_min, pt(1)); + y_max = std::max(y_max, pt(1)); + } + + type = Type::Rectangular; + rectSize = Vec2d(x_max - x_min, y_max - y_min); + rectOrigin = Vec2d(-x_min, -y_min); + + return; + } + } + + // is this a circle ? + { + // Analyze the array of points.Do they reside on a circle ? + auto center = polygon.bounding_box().center(); + std::vector vertex_distances; + double avg_dist = 0; + for (auto pt : polygon.points) + { + double distance = (pt - center).cast().norm(); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + bool defined_value = true; + for (auto el : vertex_distances) + { + if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + defined_value = false; + break; + } + if (defined_value) { + // all vertices are equidistant to center + type = Type::Circular; + diameter = unscale(avg_dist * 2); + + return; + } + } + + if (points.size() < 3) { + type = Type::Invalid; + return; + } + + // This is a custom bed shape, use the polygon provided. + type = Type::Custom; + } + + static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); + static wxString get_name(Type type); + + wxString get_full_name_with_params(); + + Type type = Type::Invalid; + Vec2d rectSize; + Vec2d rectOrigin; + + double diameter; +}; + class BedShapePanel : public wxPanel { static const std::string NONE; @@ -24,6 +119,7 @@ class BedShapePanel : public wxPanel Bed_2D* m_canvas; std::vector m_shape; std::vector m_loaded_shape; + std::string m_custom_shape; std::string m_custom_texture; std::string m_custom_model; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index bebc01c78..079bd9922 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -732,16 +732,14 @@ static std::string get_pure_opt_key(std::string opt_key) static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) { - int opt_idx = get_id_from_opt_key(opt_key); opt_key = get_pure_opt_key(opt_key); if (config.option(opt_key)->is_nil()) return _L("N/A"); + int opt_idx = get_id_from_opt_key(opt_key); wxString out; - // FIXME controll, if opt_key has index - const ConfigOptionDef* opt = config.def()->get(opt_key); bool is_nullable = opt->nullable; From 8af49d7d877d5bdf1dac3c0c034c90eb6e3086fa Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Aug 2020 15:35:58 +0200 Subject: [PATCH 24/25] Code refactoring for last commit --- src/slic3r/GUI/BedShapeDialog.cpp | 319 +++++++++++------------- src/slic3r/GUI/BedShapeDialog.hpp | 86 +------ src/slic3r/GUI/UnsavedChangesDialog.cpp | 17 +- 3 files changed, 167 insertions(+), 255 deletions(-) diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 98bd9a267..2fc7d1036 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -21,44 +21,72 @@ namespace Slic3r { namespace GUI { -void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) +BedShape::BedShape(const ConfigOptionPoints& points) { - SetFont(wxGetApp().normal_font()); - m_panel = new BedShapePanel(this); - m_panel->build_panel(default_pt, custom_texture, custom_model); + auto polygon = Polygon::new_scale(points.values); - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(m_panel, 1, wxEXPAND); - main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + // is this a rectangle ? + if (points.size() == 4) { + auto lines = polygon.lines(); + if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { + // okay, it's a rectangle + // find origin + coordf_t x_min, x_max, y_min, y_max; + x_max = x_min = points.values[0](0); + y_max = y_min = points.values[0](1); + for (auto pt : points.values) + { + x_min = std::min(x_min, pt(0)); + x_max = std::max(x_max, pt(0)); + y_min = std::min(y_min, pt(1)); + y_max = std::max(y_max, pt(1)); + } - SetSizer(main_sizer); - SetMinSize(GetSize()); - main_sizer->SetSizeHints(this); + m_type = Type::Rectangular; + m_rectSize = Vec2d(x_max - x_min, y_max - y_min); + m_rectOrigin = Vec2d(-x_min, -y_min); - this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) { - EndModal(wxID_CANCEL); - })); + return; + } + } + + // is this a circle ? + { + // Analyze the array of points.Do they reside on a circle ? + auto center = polygon.bounding_box().center(); + std::vector vertex_distances; + double avg_dist = 0; + for (auto pt : polygon.points) + { + double distance = (pt - center).cast().norm(); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + bool defined_value = true; + for (auto el : vertex_distances) + { + if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + defined_value = false; + break; + } + if (defined_value) { + // all vertices are equidistant to center + m_type = Type::Circular; + m_diameter = unscale(avg_dist * 2); + + return; + } + } + + if (points.size() < 3) + return; + + // This is a custom bed shape, use the polygon provided. + m_type = Type::Custom; } -void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) -{ - const int& em = em_unit(); - m_panel->m_shape_options_book->SetMinSize(wxSize(25 * em, -1)); - - for (auto og : m_panel->m_optgroups) - og->msw_rescale(); - - const wxSize& size = wxSize(50 * em, -1); - - SetMinSize(size); - SetSize(size); - - Refresh(); -} - -const std::string BedShapePanel::NONE = "None"; -const std::string BedShapePanel::EMPTY_STRING = ""; - static std::string get_option_label(BedShape::Parameter param) { switch (param) { @@ -118,22 +146,73 @@ wxString BedShape::get_name(Type type) } } +size_t BedShape::get_type() +{ + return static_cast(m_type == Type::Invalid ? Type::Rectangular : m_type); +} + wxString BedShape::get_full_name_with_params() { - wxString out = _L("Shape") + ": " + get_name(type); + wxString out = _L("Shape") + ": " + get_name(m_type); - if (type == Type::Rectangular) { - out += "\n" + get_option_label(Parameter::RectSize) + +": [" + ConfigOptionPoint(rectSize).serialize() + "]"; - out += "\n" + get_option_label(Parameter::RectOrigin) + +": [" + ConfigOptionPoint(rectOrigin).serialize() + "]"; + if (m_type == Type::Rectangular) { + out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]"; + out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]"; } - else if (type == Type::Circular) - out += "\n" + get_option_label(Parameter::Diameter) + +": [" + double_to_string(diameter) + "]"; - else if (type == Type::Custom) - out += "\n" + double_to_string(diameter); + else if (m_type == Type::Circular) + out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]"; return out; } +void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) +{ + if (m_type == Type::Rectangular || m_type == Type::Invalid) { + optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize }); + optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin }); + } + else if (m_type == Type::Circular) + optgroup->set_value("diameter", double_to_string(m_diameter)); +} + +void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) +{ + SetFont(wxGetApp().normal_font()); + m_panel = new BedShapePanel(this); + m_panel->build_panel(default_pt, custom_texture, custom_model); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(m_panel, 1, wxEXPAND); + main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); + + SetSizer(main_sizer); + SetMinSize(GetSize()); + main_sizer->SetSizeHints(this); + + this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) { + EndModal(wxID_CANCEL); + })); +} + +void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + const int& em = em_unit(); + m_panel->m_shape_options_book->SetMinSize(wxSize(25 * em, -1)); + + for (auto og : m_panel->m_optgroups) + og->msw_rescale(); + + const wxSize& size = wxSize(50 * em, -1); + + SetMinSize(size); + SetSize(size); + + Refresh(); +} + +const std::string BedShapePanel::NONE = "None"; +const std::string BedShapePanel::EMPTY_STRING = ""; + void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { m_shape = default_pt.values; @@ -147,36 +226,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); sbsizer->Add(m_shape_options_book); -/* auto optgroup = init_shape_options_page(_(L("Rectangular"))); - ConfigOptionDef def; - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); - def.min = 0; - def.max = 1200; - def.label = L("Size"); - def.tooltip = L("Size in X and Y of the rectangular plate."); - Option option(def, "rect_size"); - optgroup->append_single_option_line(option); - - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); - def.min = -600; - def.max = 600; - def.label = L("Origin"); - def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); - option = Option(def, "rect_origin"); - optgroup->append_single_option_line(option); - - optgroup = init_shape_options_page(_(L("Circular"))); - def.type = coFloat; - def.set_default_value(new ConfigOptionFloat(200)); - def.sidetext = L("mm"); - def.label = L("Diameter"); - def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); - option = Option(def, "diameter"); - optgroup->append_single_option_line(option); -*/ - auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular)); BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); @@ -184,7 +233,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular)); BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); -// optgroup = init_shape_options_page(_(L("Custom"))); optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom)); Line line{ "", "" }; @@ -233,10 +281,6 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf update_preview(); } -#define SHAPE_RECTANGULAR 0 -#define SHAPE_CIRCULAR 1 -#define SHAPE_CUSTOM 2 - // Called from the constructor. // Create a panel for a rectangular / circular / custom bed shape. ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) @@ -421,83 +465,18 @@ wxPanel* BedShapePanel::init_model_panel() // with the list of points in the ini file directly. void BedShapePanel::set_shape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points.values); + BedShape shape(points); - // is this a rectangle ? - if (points.size() == 4) { - auto lines = polygon.lines(); - if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { - // okay, it's a rectangle - // find origin - coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points.values[0](0); - y_max = y_min = points.values[0](1); - for (auto pt : points.values) - { - x_min = std::min(x_min, pt(0)); - x_max = std::max(x_max, pt(0)); - y_min = std::min(y_min, pt(1)); - y_max = std::max(y_max, pt(1)); - } + m_shape_options_book->SetSelection(shape.get_type()); + shape.apply_optgroup_values(m_optgroups[shape.get_type()]); - auto origin = new ConfigOptionPoints{ Vec2d(-x_min, -y_min) }; + // Copy the polygon to the canvas, make a copy of the array, if custom shape is selected + if (shape.is_custom()) + m_loaded_shape = points.values; - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]); - optgroup->set_value("rect_origin", origin); - update_shape(); - return; - } - } - - // is this a circle ? - { - // Analyze the array of points.Do they reside on a circle ? - auto center = polygon.bounding_box().center(); - std::vector vertex_distances; - double avg_dist = 0; - for (auto pt: polygon.points) - { - double distance = (pt - center).cast().norm(); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - bool defined_value = true; - for (auto el: vertex_distances) - { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) - defined_value = false; - break; - } - if (defined_value) { - // all vertices are equidistant to center - m_shape_options_book->SetSelection(SHAPE_CIRCULAR); - auto optgroup = m_optgroups[SHAPE_CIRCULAR]; - boost::any ret = wxNumberFormatter::ToString(unscale(avg_dist * 2), 0); - optgroup->set_value("diameter", ret); - update_shape(); - return; - } - } - - if (points.size() < 3) { - // Invalid polygon.Revert to default bed dimensions. - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) }); - optgroup->set_value("rect_origin", new ConfigOptionPoints{ Vec2d(0, 0) }); - update_shape(); - return; - } - - // This is a custom bed shape, use the polygon provided. - m_shape_options_book->SetSelection(SHAPE_CUSTOM); - // Copy the polygon to the canvas, make a copy of the array. - m_loaded_shape = points.values; update_shape(); + + return; } void BedShapePanel::update_preview() @@ -510,21 +489,20 @@ void BedShapePanel::update_preview() void BedShapePanel::update_shape() { auto page_idx = m_shape_options_book->GetSelection(); - if (page_idx == SHAPE_RECTANGULAR) { + auto opt_group = m_optgroups[page_idx]; + + BedShape::Type page_type = static_cast(page_idx); + + if (page_type == BedShape::Type::Rectangular) { Vec2d rect_size(Vec2d::Zero()); Vec2d rect_origin(Vec2d::Zero()); - try{ - rect_size = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); } - catch (const std::exception & /* e */) { - return; - } - try { - rect_origin = boost::any_cast(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin")); - } - catch (const std::exception & /* e */) { - return; - } - + + try { rect_size = boost::any_cast(opt_group->get_value("rect_size")); } + catch (const std::exception& /* e */) { return; } + + try { rect_origin = boost::any_cast(opt_group->get_value("rect_origin")); } + catch (const std::exception & /* e */) { return; } + auto x = rect_size(0); auto y = rect_size(1); // empty strings or '-' or other things @@ -546,14 +524,11 @@ void BedShapePanel::update_shape() Vec2d(x1, y1), Vec2d(x0, y1) }; } - else if(page_idx == SHAPE_CIRCULAR) { + else if (page_type == BedShape::Type::Circular) { double diameter; - try{ - diameter = boost::any_cast(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter")); - } - catch (const std::exception & /* e */) { - return; - } + try { diameter = boost::any_cast(opt_group->get_value("diameter")); } + catch (const std::exception & /* e */) { return; } + if (diameter == 0.0) return ; auto r = diameter / 2; auto twopi = 2 * PI; @@ -565,7 +540,7 @@ void BedShapePanel::update_shape() } m_shape = points; } - else if (page_idx == SHAPE_CUSTOM) + else if (page_type == BedShape::Type::Custom) m_shape = m_loaded_shape; update_preview(); @@ -578,8 +553,8 @@ void BedShapePanel::load_stl() if (dialog.ShowModal() != wxID_OK) return; - m_custom_shape = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(m_custom_shape, ".stl")) + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".stl")) { show_error(this, _(L("Invalid file format."))); return; @@ -589,7 +564,7 @@ void BedShapePanel::load_stl() Model model; try { - model = Model::read_from_file(m_custom_shape); + model = Model::read_from_file(file_name); } catch (std::exception &) { show_error(this, _(L("Error! Invalid model"))); diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 9064e7ddc..2cfbc73ae 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -17,8 +17,8 @@ class ConfigOptionsGroup; using ConfigOptionsGroupShp = std::shared_ptr; -struct BedShape { - +struct BedShape +{ enum class Type { Rectangular = 0, Circular, @@ -32,83 +32,24 @@ struct BedShape { Diameter }; - BedShape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points.values); + BedShape(const ConfigOptionPoints& points); - // is this a rectangle ? - if (points.size() == 4) { - auto lines = polygon.lines(); - if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { - // okay, it's a rectangle - // find origin - coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points.values[0](0); - y_max = y_min = points.values[0](1); - for (auto pt : points.values) - { - x_min = std::min(x_min, pt(0)); - x_max = std::max(x_max, pt(0)); - y_min = std::min(y_min, pt(1)); - y_max = std::max(y_max, pt(1)); - } - - type = Type::Rectangular; - rectSize = Vec2d(x_max - x_min, y_max - y_min); - rectOrigin = Vec2d(-x_min, -y_min); - - return; - } - } - - // is this a circle ? - { - // Analyze the array of points.Do they reside on a circle ? - auto center = polygon.bounding_box().center(); - std::vector vertex_distances; - double avg_dist = 0; - for (auto pt : polygon.points) - { - double distance = (pt - center).cast().norm(); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - bool defined_value = true; - for (auto el : vertex_distances) - { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) - defined_value = false; - break; - } - if (defined_value) { - // all vertices are equidistant to center - type = Type::Circular; - diameter = unscale(avg_dist * 2); - - return; - } - } - - if (points.size() < 3) { - type = Type::Invalid; - return; - } - - // This is a custom bed shape, use the polygon provided. - type = Type::Custom; - } + bool is_custom() { return m_type == Type::Custom; } static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); static wxString get_name(Type type); + // convert Type to size_t + size_t get_type(); + wxString get_full_name_with_params(); + void apply_optgroup_values(ConfigOptionsGroupShp optgroup); - Type type = Type::Invalid; - Vec2d rectSize; - Vec2d rectOrigin; - - double diameter; +private: + Type m_type {Type::Invalid}; + Vec2d m_rectSize {200, 200}; + Vec2d m_rectOrigin {0, 0}; + double m_diameter {0}; }; class BedShapePanel : public wxPanel @@ -119,7 +60,6 @@ class BedShapePanel : public wxPanel Bed_2D* m_canvas; std::vector m_shape; std::vector m_loaded_shape; - std::string m_custom_shape; std::string m_custom_texture; std::string m_custom_model; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 079bd9922..c147d3e2c 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -667,10 +667,10 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name text = _L("All changed options will be reverted."); else { if (action == Action::Save && preset_name.empty()) - text = _L("After press this button selected options will be saved"); + text = _L("Press to save the selected options"); else { std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); - text = from_u8((boost::format("After press this button selected options will be %1% to the preset \"%2%\".") % act_string % preset_name).str()); + text = from_u8((boost::format("Press to %1% selected options to the preset \"%2%\".") % act_string % preset_name).str()); } text += "\n" + _L("Unselected options will be reverted."); } @@ -732,12 +732,12 @@ static std::string get_pure_opt_key(std::string opt_key) static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& config) { + int opt_idx = get_id_from_opt_key(opt_key); opt_key = get_pure_opt_key(opt_key); if (config.option(opt_key)->is_nil()) return _L("N/A"); - int opt_idx = get_id_from_opt_key(opt_key); wxString out; const ConfigOptionDef* opt = config.def()->get(opt_key); @@ -820,15 +820,12 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& break; } case coPoints: { - /* if (opt_key == "bed_shape") { - config.option(opt_key)->values = boost::any_cast>(value); - break; + BedShape shape(*config.option(opt_key)); + return shape.get_full_name_with_params(); } - ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - */ - return "Points"; + Vec2d val = config.opt(opt_key)->get_at(opt_idx); + return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); } default: break; From d8487b1458eb768439a16bb255d82b71064a5f96 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Sep 2020 09:06:42 +0200 Subject: [PATCH 25/25] Unsaved Changes: bug fix and improvements - changed width of the "Save dialog" - SavePresetDialog: added info for Print/Filament user presets incompatible with selected printer_technology - fixed missed "modified" suffix when options are moved to the another preset - "move selected options" button is added for dependent presets --- src/slic3r/GUI/PresetComboBoxes.cpp | 8 ++-- src/slic3r/GUI/Tab.cpp | 51 +++++++++++++++++-------- src/slic3r/GUI/Tab.hpp | 5 ++- src/slic3r/GUI/UnsavedChangesDialog.cpp | 22 ++++++----- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 9b0c9d0c8..7300a2887 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1059,7 +1059,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); - m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name)); + m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); for (const std::string& value : values) m_combo->Append(from_u8(value)); @@ -1131,8 +1131,10 @@ void SavePresetDialog::Item::update() if (m_valid_type == Valid && existing && m_preset_name != m_presets->get_selected_preset_name()) { - info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()) + "\n" + - _L("Note: This preset will be replaced after saving"); + info_line = from_u8((boost::format(_u8L("Preset with name \"%1%\" already exists.")) % m_preset_name).str()); + if (!existing->is_compatible) + info_line += "\n" + _L("And selected preset is imcopatible with selected printer."); + info_line += "\n" + _L("Note: This preset will be replaced after saving"); m_valid_type = Warning; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 898890f6e..b95227dad 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1103,6 +1103,21 @@ void Tab::apply_searcher() wxGetApp().sidebar().get_searcher().apply(m_config, m_type, m_mode); } +void Tab::cache_config_diff(const std::vector& selected_options) +{ + m_cache_config.apply_only(m_presets->get_edited_preset().config, selected_options); +} + +void Tab::apply_config_from_cache() +{ + if (!m_cache_config.empty()) { + m_presets->get_edited_preset().config.apply(m_cache_config); + m_cache_config.clear(); + + update_dirty(); + } +} + // Call a callback to update the selection of presets on the plater: // To update the content of the selection boxes, @@ -1122,9 +1137,12 @@ void Tab::on_presets_changed() // Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. for (auto t: m_dependent_tabs) { + Tab* tab = wxGetApp().get_tab(t); // If the printer tells us that the print or filament/sla_material preset has been switched or invalidated, // refresh the print or filament/sla_material tab page. - wxGetApp().get_tab(t)->load_current_preset(); + // But if there are options, moved from the previously selected preset, update them to edited preset + tab->apply_config_from_cache(); + tab->load_current_preset(); } // clear m_dependent_tabs after first update from select_preset() // to avoid needless preset loading from update() function @@ -3136,10 +3154,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, static_cast(this)->apply_extruder_cnt_from_cache(); // check if there is something in the cache to move to the new selected preset - if (!m_cache_config.empty()) { - m_presets->get_edited_preset().config.apply(m_cache_config); - m_cache_config.clear(); - } + apply_config_from_cache(); load_current_preset(); } @@ -3189,17 +3204,23 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr else if (dlg.move_preset()) // move selected changes { std::vector selected_options = dlg.get_selected_options(); - auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); - if (it != selected_options.end()) { - // erase "extruders_count" option from the list - selected_options.erase(it); - // cache the extruders count - if (m_type == Preset::TYPE_PRINTER) - static_cast(this)->cache_extruder_cnt(); - } + if (m_type == presets->type()) // move changes for the current preset from this tab + { + if (m_type == Preset::TYPE_PRINTER) { + auto it = std::find(selected_options.begin(), selected_options.end(), "extruders_count"); + if (it != selected_options.end()) { + // erase "extruders_count" option from the list + selected_options.erase(it); + // cache the extruders count + static_cast(this)->cache_extruder_cnt(); + } + } - // copy selected options to the cache from edited preset - m_cache_config.apply_only(*m_config, selected_options); + // copy selected options to the cache from edited preset + cache_config_diff(selected_options); + } + else + wxGetApp().get_tab(presets->type())->cache_config_diff(selected_options); } return true; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 9bddebeab..f0b2e97b3 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -231,12 +231,13 @@ protected: } m_highlighter; + DynamicPrintConfig m_cache_config; + public: PresetBundle* m_preset_bundle; bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets; DynamicPrintConfig* m_config; - DynamicPrintConfig m_cache_config; ogStaticText* m_parent_preset_description_line; ScalableButton* m_detach_preset_btn = nullptr; @@ -330,6 +331,8 @@ public: void update_wiping_button_visibility(); void activate_option(const std::string& opt_key, const wxString& category); void apply_searcher(); + void cache_config_diff(const std::vector& selected_options); + void apply_config_from_cache(); protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index c147d3e2c..f30e719ce 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -590,8 +590,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ int btn_idx = 0; add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, btn_idx++); - if (type != Preset::TYPE_INVALID && type == dependent_presets->type() && - dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology()) + if (dependent_presets && (type != dependent_presets->type() ? true : + dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology())) add_btn(&m_move_btn, m_move_btn_id, "paste_menu", Action::Move, btn_idx++); add_btn(&m_continue_btn, m_continue_btn_id, "cross", Action::Continue, btn_idx, false); @@ -666,12 +666,11 @@ void UnsavedChangesDialog::show_info_line(Action action, std::string preset_name else if (action == Action::Continue) text = _L("All changed options will be reverted."); else { - if (action == Action::Save && preset_name.empty()) - text = _L("Press to save the selected options"); - else { - std::string act_string = action == Action::Save ? _u8L("saved") : _u8L("moved"); + std::string act_string = action == Action::Save ? _u8L("save") : _u8L("move"); + if (preset_name.empty()) + text = from_u8((boost::format("Press to %1% selected options.") % act_string).str()); + else text = from_u8((boost::format("Press to %1% selected options to the preset \"%2%\".") % act_string % preset_name).str()); - } text += "\n" + _L("Unselected options will be reverted."); } m_info_line->SetLabel(text); @@ -856,8 +855,10 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent // activate buttons and labels m_save_btn ->Bind(wxEVT_ENTER_WINDOW, [this, presets] (wxMouseEvent& e) { show_info_line(Action::Save, presets ? presets->get_selected_preset().name : ""); e.Skip(); }); - if (m_move_btn) - m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset] (wxMouseEvent& e) { show_info_line(Action::Move, new_selected_preset); e.Skip(); }); + if (m_move_btn) { + bool is_empty_name = type != dependent_presets->type(); + m_move_btn ->Bind(wxEVT_ENTER_WINDOW, [this, new_selected_preset, is_empty_name] (wxMouseEvent& e) { show_info_line(Action::Move, is_empty_name ? "" : new_selected_preset); e.Skip(); }); + } m_continue_btn ->Bind(wxEVT_ENTER_WINDOW, [this] (wxMouseEvent& e) { show_info_line(Action::Continue); e.Skip(); }); m_continue_btn->SetLabel(_L("Continue without changes")); @@ -879,6 +880,9 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent _L("is not compatible with print profile"); action_msg += " \"" + from_u8(new_selected_preset) + "\"\n"; action_msg += _L("and it has the following unsaved changes:"); + + if (m_move_btn) + m_move_btn->SetLabel(_L("Move selected to the first compatible preset")); } m_action_line->SetLabel(from_u8((boost::format(_utf8(L("Preset \"%1%\" %2%"))) % _utf8(presets->get_edited_preset().name) % action_msg).str())); m_save_btn->SetLabel(from_u8((boost::format(_u8L("Save selected to preset: %1%")) % ("\"" + presets->get_selected_preset().name + "\"")).str()));