UnsavedChangesDialog:
first implementation
This commit is contained in:
parent
5eb3b21be7
commit
3cf2914a9e
6 changed files with 437 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
271
src/slic3r/GUI/UnsavedChangesDialog.cpp
Normal file
271
src/slic3r/GUI/UnsavedChangesDialog.cpp
Normal 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
|
151
src/slic3r/GUI/UnsavedChangesDialog.hpp
Normal file
151
src/slic3r/GUI/UnsavedChangesDialog.hpp
Normal 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_
|
Loading…
Reference in a new issue