Merge remote-tracking branch 'origin/ys_unsaved_changes'
This commit is contained in:
commit
29f2ac3ad4
23 changed files with 2397 additions and 616 deletions
|
@ -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<std::string>& 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)) {
|
||||
|
|
|
@ -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<std::string>& unselected_options);
|
||||
|
||||
static const char *PRUSA_BUNDLE;
|
||||
private:
|
||||
std::string load_system_presets();
|
||||
|
|
|
@ -173,6 +173,10 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Search.hpp
|
||||
GUI/NotificationManager.cpp
|
||||
GUI/NotificationManager.hpp
|
||||
GUI/UnsavedChangesDialog.cpp
|
||||
GUI/UnsavedChangesDialog.hpp
|
||||
GUI/ExtraRenderers.cpp
|
||||
GUI/ExtraRenderers.hpp
|
||||
Utils/Http.cpp
|
||||
Utils/Http.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
|
|
|
@ -21,6 +21,160 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
BedShape::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));
|
||||
}
|
||||
|
||||
m_type = Type::Rectangular;
|
||||
m_rectSize = Vec2d(x_max - x_min, y_max - y_min);
|
||||
m_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<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt : polygon.points)
|
||||
{
|
||||
double distance = (pt - center).cast<double>().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<double>(avg_dist * 2);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (points.size() < 3)
|
||||
return;
|
||||
|
||||
// This is a custom bed shape, use the polygon provided.
|
||||
m_type = Type::Custom;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
size_t BedShape::get_type()
|
||||
{
|
||||
return static_cast<size_t>(m_type == Type::Invalid ? Type::Rectangular : m_type);
|
||||
}
|
||||
|
||||
wxString BedShape::get_full_name_with_params()
|
||||
{
|
||||
wxString out = _L("Shape") + ": " + get_name(m_type);
|
||||
|
||||
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 (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());
|
||||
|
@ -72,50 +226,28 @@ 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);
|
||||
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);
|
||||
|
||||
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(BedShape::get_name(BedShape::Type::Circular));
|
||||
BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter);
|
||||
|
||||
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);
|
||||
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;
|
||||
};
|
||||
|
@ -149,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)
|
||||
|
@ -337,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<double> vertex_distances;
|
||||
double avg_dist = 0;
|
||||
for (auto pt: polygon.points)
|
||||
{
|
||||
double distance = (pt - center).cast<double>().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<double>(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()
|
||||
|
@ -426,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<BedShape::Type>(page_idx);
|
||||
|
||||
if (page_type == BedShape::Type::Rectangular) {
|
||||
Vec2d rect_size(Vec2d::Zero());
|
||||
Vec2d rect_origin(Vec2d::Zero());
|
||||
try{
|
||||
rect_size = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); }
|
||||
catch (const std::exception & /* e */) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
rect_origin = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin"));
|
||||
}
|
||||
catch (const std::exception & /* e */) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try { rect_size = boost::any_cast<Vec2d>(opt_group->get_value("rect_size")); }
|
||||
catch (const std::exception& /* e */) { return; }
|
||||
|
||||
try { rect_origin = boost::any_cast<Vec2d>(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
|
||||
|
@ -462,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<double>(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter"));
|
||||
}
|
||||
catch (const std::exception & /* e */) {
|
||||
return;
|
||||
}
|
||||
try { diameter = boost::any_cast<double>(opt_group->get_value("diameter")); }
|
||||
catch (const std::exception & /* e */) { return; }
|
||||
|
||||
if (diameter == 0.0) return ;
|
||||
auto r = diameter / 2;
|
||||
auto twopi = 2 * PI;
|
||||
|
@ -481,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();
|
||||
|
|
|
@ -16,6 +16,42 @@ namespace GUI {
|
|||
class ConfigOptionsGroup;
|
||||
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
|
||||
struct BedShape
|
||||
{
|
||||
enum class Type {
|
||||
Rectangular = 0,
|
||||
Circular,
|
||||
Custom,
|
||||
Invalid
|
||||
};
|
||||
|
||||
enum class Parameter {
|
||||
RectSize,
|
||||
RectOrigin,
|
||||
Diameter
|
||||
};
|
||||
|
||||
BedShape(const ConfigOptionPoints& points);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
Type m_type {Type::Invalid};
|
||||
Vec2d m_rectSize {200, 200};
|
||||
Vec2d m_rectOrigin {0, 0};
|
||||
double m_diameter {0};
|
||||
};
|
||||
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
static const std::string NONE;
|
||||
|
|
333
src/slic3r/GUI/ExtraRenderers.cpp
Normal file
333
src/slic3r/GUI/ExtraRenderers.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
#include "ExtraRenderers.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <wx/dc.h>
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#include "wx/generic/private/markuptext.h"
|
||||
#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
|
||||
|
||||
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
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
if (m_markupText)
|
||||
delete m_markupText;
|
||||
#endif //wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#endif // SUPPORTS_MARKUP
|
||||
}
|
||||
|
||||
void BitmapTextRenderer::EnableMarkup(bool enable)
|
||||
{
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
if (enable) {
|
||||
if (!m_markupText)
|
||||
m_markupText = new wxItemMarkupText(wxString());
|
||||
}
|
||||
else {
|
||||
if (m_markupText) {
|
||||
delete m_markupText;
|
||||
m_markupText = nullptr;
|
||||
}
|
||||
}
|
||||
#else
|
||||
is_markupText = enable;
|
||||
#endif //wxHAS_GENERIC_DATAVIEWCTRL
|
||||
#endif // SUPPORTS_MARKUP
|
||||
}
|
||||
|
||||
bool BitmapTextRenderer::SetValue(const wxVariant &value)
|
||||
{
|
||||
m_value << value;
|
||||
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
if (m_markupText)
|
||||
m_markupText->SetMarkup(m_value.GetText());
|
||||
/*
|
||||
#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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL)
|
||||
if (m_markupText)
|
||||
{
|
||||
rect.x += xoffset;
|
||||
m_markupText->Render(GetView(), *dc, rect, 0, GetEllipsizeMode());
|
||||
}
|
||||
else
|
||||
#endif // SUPPORTS_MARKUP && wxHAS_GENERIC_DATAVIEWCTRL
|
||||
RenderText(m_value.GetText(), xoffset, rect, dc, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxSize BitmapTextRenderer::GetSize() const
|
||||
{
|
||||
if (!m_value.GetText().empty())
|
||||
{
|
||||
wxSize size;
|
||||
#if defined(SUPPORTS_MARKUP) && defined(wxHAS_GENERIC_DATAVIEWCTRL)
|
||||
if (m_markupText)
|
||||
{
|
||||
wxDataViewCtrl* const view = GetView();
|
||||
wxClientDC dc(view);
|
||||
if (GetAttr().HasFont())
|
||||
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());
|
||||
|
||||
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 && !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 && !can_create_editor_ctrl())
|
||||
return nullptr;
|
||||
|
||||
std::vector<wxBitmap*> 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;
|
||||
}
|
||||
|
||||
|
160
src/slic3r/GUI/ExtraRenderers.hpp
Normal file
160
src/slic3r/GUI/ExtraRenderers.hpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
#ifndef slic3r_GUI_ExtraRenderers_hpp_
|
||||
#define slic3r_GUI_ExtraRenderers_hpp_
|
||||
|
||||
#include <wx/dataview.h>
|
||||
|
||||
#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(bool use_markup = false,
|
||||
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)
|
||||
{
|
||||
EnableMarkup(use_markup);
|
||||
}
|
||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
~BitmapTextRenderer();
|
||||
|
||||
void EnableMarkup(bool enable = true);
|
||||
|
||||
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
|
||||
|
||||
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<bool()> can_create_fn) { can_create_editor_ctrl = can_create_fn; }
|
||||
|
||||
private:
|
||||
DataViewBitmapText m_value;
|
||||
bool m_was_unusable_symbol{ false };
|
||||
|
||||
std::function<bool()> can_create_editor_ctrl { nullptr };
|
||||
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
|
||||
class wxItemMarkupText* m_markupText { nullptr };;
|
||||
#else
|
||||
bool is_markupText {false};
|
||||
#endif
|
||||
#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<bool()> can_create_fn) { can_create_editor_ctrl = can_create_fn; }
|
||||
|
||||
private:
|
||||
DataViewBitmapText m_value;
|
||||
std::function<bool()> can_create_editor_ctrl { nullptr };
|
||||
};
|
||||
|
||||
|
||||
#endif // slic3r_GUI_ExtraRenderers_hpp_
|
|
@ -58,6 +58,8 @@
|
|||
#include "RemovableDriveManager.hpp"
|
||||
#include "InstanceCheck.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
#include "PresetComboBoxes.hpp"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
#include <dbt.h>
|
||||
|
@ -1173,29 +1175,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<NameType> names_and_types;
|
||||
|
||||
// for system/default/external presets we should take an edited name
|
||||
std::vector<Preset::Type> 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)
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
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.
|
||||
|
|
|
@ -1549,245 +1549,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
|
||||
|
||||
bool BitmapTextRenderer::SetValue(const wxVariant &value)
|
||||
{
|
||||
m_value << value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
|
||||
wxString BitmapTextRenderer::GetAccessibleDescription() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
RenderText(m_value.GetText(), xoffset, rect, dc, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxSize BitmapTextRenderer::GetSize() const
|
||||
{
|
||||
if (!m_value.GetText().empty())
|
||||
{
|
||||
wxSize size = GetTextExtent(m_value.GetText());
|
||||
|
||||
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<ObjectDataViewModel*>(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<ObjectDataViewModel*>(dv_ctrl->GetModel());
|
||||
|
||||
if (!(model->GetItemType(dv_ctrl->GetSelection()) & (itVolume | itLayer | itObject)))
|
||||
return nullptr;
|
||||
|
||||
std::vector<wxBitmap*> 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
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
#define slic3r_GUI_ObjectDataViewModel_hpp_
|
||||
|
||||
#include <wx/dataview.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ExtraRenderers.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
@ -14,143 +15,6 @@ namespace GUI {
|
|||
typedef double coordf_t;
|
||||
typedef std::pair<coordf_t, coordf_t> 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)
|
||||
{}
|
||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -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<ConfigOptionStrings>(opt_key)->values;
|
||||
break;
|
||||
}
|
||||
if (opt_key == "filament_ramming_parameters") {
|
||||
ret = config.opt_string(opt_key, static_cast<unsigned int>(idx));
|
||||
break;
|
||||
}
|
||||
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
||||
ret = text_value;
|
||||
else if (opt->gui_flags.compare("serialized") == 0) {
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
@ -1123,17 +1123,26 @@ 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" +
|
||||
_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;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
|
@ -1163,8 +1172,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<Preset::Type>{type}, suffix);
|
||||
}
|
||||
|
||||
SavePresetDialog::SavePresetDialog(std::vector<Preset::Type> 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<Preset::Type> types, std::string suffix)
|
||||
{
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
#if ENABLE_WX_3_1_3_DPI_CHANGED_EVENT
|
||||
|
@ -1172,14 +1199,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);
|
||||
|
@ -1196,26 +1227,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;
|
||||
|
@ -1284,8 +1315,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));
|
||||
|
@ -1324,10 +1355,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);
|
||||
|
|
|
@ -235,7 +235,7 @@ class SavePresetDialog : public DPIDialog
|
|||
void update();
|
||||
};
|
||||
|
||||
std::vector<Item> m_items;
|
||||
std::vector<Item*> 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<Preset::Type> 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<Preset::Type> types, std::string suffix = "");
|
||||
void update_physical_printers(const std::string& preset_name);
|
||||
void accept();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
@ -327,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::lower_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};
|
||||
|
@ -442,7 +444,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
|
|||
|
||||
wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer();
|
||||
|
||||
#ifdef SEARCH_SUPPORTS_MARKUP
|
||||
#ifdef SUPPORTS_MARKUP
|
||||
markupRenderer->EnableMarkup();
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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<FoundOption>& 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; });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "PhysicalPrinterDialog.hpp"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -325,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);
|
||||
}
|
||||
|
||||
|
@ -412,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);
|
||||
|
@ -503,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);
|
||||
|
@ -655,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;
|
||||
|
@ -733,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
|
||||
|
@ -1094,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<std::string>& 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,
|
||||
|
@ -1113,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
|
||||
|
@ -1736,22 +1763,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<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0));
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
(m_config->option<ConfigOptionStrings>("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();
|
||||
|
@ -2858,7 +2884,7 @@ void Tab::load_current_preset()
|
|||
}
|
||||
on_presets_changed();
|
||||
if (printer_technology == ptFFF) {
|
||||
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;
|
||||
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<const ConfigOptionFloats*>(m_presets->get_selected_preset().config.option("nozzle_diameter"))->values.size(); //static_cast<TabPrinter*>(this)->m_extruders_count;
|
||||
const Preset* parent_preset = m_presets->get_selected_preset_parent();
|
||||
static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 :
|
||||
static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size();
|
||||
|
@ -2989,7 +3015,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
|
||||
|
@ -3123,6 +3149,13 @@ 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<TabPrinter*>(this)->apply_extruder_cnt_from_cache();
|
||||
|
||||
// check if there is something in the cache to move to the new selected preset
|
||||
apply_config_from_cache();
|
||||
|
||||
load_current_preset();
|
||||
}
|
||||
}
|
||||
|
@ -3132,41 +3165,65 @@ 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 /*= ""*/)
|
||||
{
|
||||
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();
|
||||
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;
|
||||
|
||||
// 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);
|
||||
/*std::string*/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";
|
||||
UnsavedChangesDialog dlg(m_type, presets, new_printer_name);
|
||||
if (dlg.ShowModal() == wxID_CANCEL)
|
||||
return false;
|
||||
|
||||
if (dlg.save_preset()) // save selected changes
|
||||
{
|
||||
std::vector<std::string> 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);
|
||||
if (save_dlg.ShowModal() != wxID_OK)
|
||||
return false;
|
||||
name = save_dlg.get_name();
|
||||
}
|
||||
|
||||
if (m_type == presets->type()) // save changes for the current preset from this tab
|
||||
{
|
||||
// revert unselected options to the old values
|
||||
presets->get_edited_preset().config.apply_only(presets->get_selected_preset().config, unselected_options);
|
||||
save_preset(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
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,
|
||||
* 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
|
||||
{
|
||||
std::vector<std::string> selected_options = dlg.get_selected_options();
|
||||
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<TabPrinter*>(this)->cache_extruder_cnt();
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
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).
|
||||
|
@ -3259,10 +3316,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();
|
||||
|
@ -3573,6 +3628,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<ConfigOptionStrings>(deps.key_list)->values.empty();
|
||||
|
|
|
@ -231,6 +231,8 @@ protected:
|
|||
}
|
||||
m_highlighter;
|
||||
|
||||
DynamicPrintConfig m_cache_config;
|
||||
|
||||
public:
|
||||
PresetBundle* m_preset_bundle;
|
||||
bool m_show_btn_incompatible_presets = false;
|
||||
|
@ -329,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<std::string>& selected_options);
|
||||
void apply_config_from_cache();
|
||||
|
||||
protected:
|
||||
void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget);
|
||||
|
@ -410,6 +414,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;
|
||||
|
||||
|
@ -436,6 +441,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
|
||||
|
|
1066
src/slic3r/GUI/UnsavedChangesDialog.cpp
Normal file
1066
src/slic3r/GUI/UnsavedChangesDialog.cpp
Normal file
File diff suppressed because it is too large
Load diff
272
src/slic3r/GUI/UnsavedChangesDialog.hpp
Normal file
272
src/slic3r/GUI/UnsavedChangesDialog.hpp
Normal file
|
@ -0,0 +1,272 @@
|
|||
#ifndef slic3r_UnsavedChangesDialog_hpp_
|
||||
#define slic3r_UnsavedChangesDialog_hpp_
|
||||
|
||||
#include <wx/dataview.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
|
||||
class ScalableButton;
|
||||
class wxStaticText;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI{
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ModelNode: a node inside UnsavedChangesModel
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
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 };
|
||||
|
||||
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;
|
||||
|
||||
// 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};
|
||||
|
||||
#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;
|
||||
wxBitmap m_old_color_bmp;
|
||||
wxBitmap m_new_color_bmp;
|
||||
#endif //__linux__
|
||||
wxString m_text;
|
||||
wxString m_old_value;
|
||||
wxString m_new_value;
|
||||
|
||||
// preset(root) node
|
||||
ModelNode(Preset::Type preset_type, const wxString& text, wxWindow* parent_win);
|
||||
|
||||
// category node
|
||||
ModelNode(ModelNode* parent, const wxString& text, const std::string& icon_name);
|
||||
|
||||
// group node
|
||||
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
|
||||
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 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 UpdateEnabling();
|
||||
void UpdateIcons();
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// UnsavedChangesModel
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class UnsavedChangesModel : public wxDataViewModel
|
||||
{
|
||||
wxWindow* m_parent_win { nullptr };
|
||||
std::vector<ModelNode*> 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,
|
||||
colIconText,
|
||||
colOldValue,
|
||||
colNewValue,
|
||||
colMax
|
||||
};
|
||||
|
||||
UnsavedChangesModel(wxWindow* parent);
|
||||
~UnsavedChangesModel();
|
||||
|
||||
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
|
||||
|
||||
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);
|
||||
|
||||
void UpdateItemEnabling(wxDataViewItem item);
|
||||
bool IsEnabledItem(const wxDataViewItem& item);
|
||||
|
||||
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;
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
// UnsavedChangesDialog
|
||||
//------------------------------------------
|
||||
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_action_line { 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 };
|
||||
|
||||
enum class Action {
|
||||
Undef,
|
||||
Save,
|
||||
Move,
|
||||
Continue
|
||||
};
|
||||
|
||||
// selected action after Dialog closing
|
||||
Action m_exit_action {Action::Undef};
|
||||
|
||||
// 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;
|
||||
Preset::Type type;
|
||||
bool is_long {false};
|
||||
};
|
||||
// tree items related to the options
|
||||
std::map<wxDataViewItem, ItemData> 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 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 = "");
|
||||
void close(Action action);
|
||||
|
||||
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<std::string> get_unselected_options(Preset::Type type);
|
||||
std::vector<std::string> get_selected_options();
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
void on_sys_color_changed() override;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
// FullCompareDialog
|
||||
//------------------------------------------
|
||||
class FullCompareDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value);
|
||||
~FullCompareDialog() {}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //slic3r_UnsavedChangesDialog_hpp_
|
|
@ -786,9 +786,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__
|
||||
|
@ -797,6 +799,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)
|
||||
{
|
||||
|
@ -846,11 +850,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)
|
||||
{
|
||||
|
|
|
@ -209,7 +209,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,
|
||||
|
@ -223,6 +224,7 @@ public:
|
|||
void SetBitmap_(const ScalableBitmap& bmp);
|
||||
void SetBitmapDisabled_(const ScalableBitmap &bmp);
|
||||
int GetBitmapHeight();
|
||||
void UseDefaultBitmapDisabled();
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
|
@ -233,6 +235,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 };
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue