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 }; };