UnsavedChangesDialog:

first implementation
This commit is contained in:
YuSanka 2020-07-30 16:16:56 +02:00
parent 5eb3b21be7
commit 3cf2914a9e
6 changed files with 437 additions and 8 deletions

View file

@ -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

View file

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

View file

@ -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)), "<b>");
boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd)), "</b>");
#else
@ -442,7 +436,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer();
#ifdef SEARCH_SUPPORTS_MARKUP
#ifdef SUPPORTS_MARKUP
markupRenderer->EnableMarkup();
#endif

View file

@ -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();

View file

@ -0,0 +1,271 @@
#include "UnsavedChangesDialog.hpp"
#include <cstddef>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <boost/nowide/convert.hpp>
#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

View file

@ -0,0 +1,151 @@
#ifndef slic3r_UnsavedChangesDialog_hpp_
#define slic3r_UnsavedChangesDialog_hpp_
#include <vector>
#include <map>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/listctrl.h>
#include <wx/dataview.h>
#include <wx/combo.h>
#include <wx/checkbox.h>
#include <wx/dialog.h>
#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_