Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorenricoturri1966 <enricoturri@seznam.cz>2022-01-03 12:09:38 +0300
committerenricoturri1966 <enricoturri@seznam.cz>2022-01-03 12:09:38 +0300
commit1019792884d40632de77906f06e6c1f50958d6f3 (patch)
tree7c157f43c479b64c177905889363fe7dfb0fccf5 /src/slic3r/GUI
parentf958c14db853f6cffa770531af88fdc71408fdd0 (diff)
parent86657523e38f21955a490869d276207c7ee1ec87 (diff)
Fixed conflicts after merge with dev branch
Diffstat (limited to 'src/slic3r/GUI')
-rw-r--r--src/slic3r/GUI/BedShapeDialog.cpp4
-rw-r--r--src/slic3r/GUI/BitmapComboBox.cpp4
-rw-r--r--src/slic3r/GUI/ConfigManipulation.cpp7
-rw-r--r--src/slic3r/GUI/ConfigWizard.cpp30
-rw-r--r--src/slic3r/GUI/DoubleSlider.cpp22
-rw-r--r--src/slic3r/GUI/DoubleSlider.hpp2
-rw-r--r--src/slic3r/GUI/DoubleSlider_Utils.hpp308
-rw-r--r--src/slic3r/GUI/GCodeViewer.cpp71
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp11
-rw-r--r--src/slic3r/GUI/GLShader.cpp134
-rw-r--r--src/slic3r/GUI/GLShader.hpp49
-rw-r--r--src/slic3r/GUI/GUI.cpp4
-rw-r--r--src/slic3r/GUI/GUI_App.cpp299
-rw-r--r--src/slic3r/GUI/GUI_App.hpp12
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp46
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.hpp2
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoBase.cpp9
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoBase.hpp4
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp22
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp3
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMove.cpp8
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoMove.hpp2
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp21
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp5
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp22
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp6
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp7
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp4
-rw-r--r--src/slic3r/GUI/HintNotification.cpp6
-rw-r--r--src/slic3r/GUI/MainFrame.cpp57
-rw-r--r--src/slic3r/GUI/MainFrame.hpp2
-rw-r--r--src/slic3r/GUI/MsgDialog.cpp47
-rw-r--r--src/slic3r/GUI/MsgDialog.hpp10
-rw-r--r--src/slic3r/GUI/OptionsGroup.cpp2
-rw-r--r--src/slic3r/GUI/OptionsGroup.hpp10
-rw-r--r--src/slic3r/GUI/Plater.cpp136
-rw-r--r--src/slic3r/GUI/Preferences.cpp646
-rw-r--r--src/slic3r/GUI/Preferences.hpp29
-rw-r--r--src/slic3r/GUI/Search.cpp49
-rw-r--r--src/slic3r/GUI/Search.hpp6
-rw-r--r--src/slic3r/GUI/Selection.cpp3
-rw-r--r--src/slic3r/GUI/SendSystemInfoDialog.cpp4
-rw-r--r--src/slic3r/GUI/Tab.cpp65
-rw-r--r--src/slic3r/GUI/Tab.hpp15
-rw-r--r--src/slic3r/GUI/UnsavedChangesDialog.cpp16
-rw-r--r--src/slic3r/GUI/UpdateDialogs.cpp4
-rw-r--r--src/slic3r/GUI/fts_fuzzy_match.h1
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp98
-rw-r--r--src/slic3r/GUI/wxExtensions.hpp61
49 files changed, 1299 insertions, 1086 deletions
diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp
index 2d46a5228..246c8b63e 100644
--- a/src/slic3r/GUI/BedShapeDialog.cpp
+++ b/src/slic3r/GUI/BedShapeDialog.cpp
@@ -109,7 +109,7 @@ wxString BedShape::get_full_name_with_params()
default:
// rectangle, convex, concave...
out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().size())).serialize() + "]";
- out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
+ out += "\n" + _(get_option_label(Parameter::RectOrigin)) + ": [" + ConfigOptionPoint(- to_2d(m_build_volume.bounding_volume().min)).serialize() + "]";
break;
}
return out;
@@ -124,7 +124,7 @@ void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup)
default:
// rectangle, convex, concave...
optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) });
- optgroup->set_value("rect_origin" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().min) });
+ optgroup->set_value("rect_origin" , new ConfigOptionPoints{ - to_2d(m_build_volume.bounding_volume().min) });
}
}
diff --git a/src/slic3r/GUI/BitmapComboBox.cpp b/src/slic3r/GUI/BitmapComboBox.cpp
index 24bc8dcfd..3396c627b 100644
--- a/src/slic3r/GUI/BitmapComboBox.cpp
+++ b/src/slic3r/GUI/BitmapComboBox.cpp
@@ -254,8 +254,8 @@ void BitmapComboBox::DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(
dc.SetTextForeground(flags & ODCB_PAINTING_DISABLED ? wxColour(108,108,108) : wxGetApp().get_label_clr_default());
wxColour selCol = flags & ODCB_PAINTING_DISABLED ?
-#ifdef _MSW_DAEK_MODE
- wxRGBToColour(NppDarkMode::InvertLightnessSofter(NppDarkMode::GetBackgroundColor())) :
+#ifdef _MSW_DARK_MODE
+ wxRGBToColour(NppDarkMode::GetSofterBackgroundColor()) :
#else
wxGetApp().get_highlight_default_clr() :
#endif
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index fe108b1a6..8defc4554 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -155,7 +155,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
apply(config, &new_conf);
}
- if (config->opt_bool("support_material")) {
+ // Check "support_material" and "overhangs" relations only on global settings level
+ if (is_global_config && config->opt_bool("support_material")) {
// Ask only once.
if (!m_support_material_overhangs_queried) {
m_support_material_overhangs_queried = true;
@@ -164,10 +165,10 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
"- Detect bridging perimeters"));
if (is_global_config)
msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?"));
- MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Support Generator"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
+ MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Support Generator"), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *config;
auto answer = dialog.ShowModal();
- if (!is_global_config || answer == wxID_YES) {
+ if (answer == wxID_YES) {
// Enable "detect bridging perimeters".
new_conf.set_key_value("overhangs", new ConfigOptionBool(true));
}
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 579481201..bcc4ff3fa 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -627,7 +627,7 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
list_printer->SetMinSize(wxSize(23*em, list_h));
- list_type->SetMinSize(wxSize(8*em, list_h));
+ list_type->SetMinSize(wxSize(13*em, list_h));
list_vendor->SetMinSize(wxSize(13*em, list_h));
list_profile->SetMinSize(wxSize(23*em, list_h));
@@ -2522,23 +2522,33 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
{
wxString header, caption = _L("Configuration is edited in ConfigWizard");
const auto enabled_vendors = appconfig_new.vendors();
+ const auto enabled_vendors_old = app_config->vendors();
bool suppress_sla_printer = model_has_multi_part_objects(wxGetApp().model());
PrinterTechnology preferred_pt = ptAny;
- auto get_preferred_printer_technology = [enabled_vendors, suppress_sla_printer](const std::string& bundle_name, const Bundle& bundle) {
+ auto get_preferred_printer_technology = [enabled_vendors, enabled_vendors_old, suppress_sla_printer](const std::string& bundle_name, const Bundle& bundle) {
const auto config = enabled_vendors.find(bundle_name);
PrinterTechnology pt = ptAny;
if (config != enabled_vendors.end()) {
for (const auto& model : bundle.vendor_profile->models) {
if (const auto model_it = config->second.find(model.id);
model_it != config->second.end() && model_it->second.size() > 0) {
- if (pt == ptAny)
- pt = model.technology;
- // if preferred printer model has SLA printer technology it's important to check the model for multypart state
- if (pt == ptSLA && suppress_sla_printer)
- continue;
- else
+ pt = model.technology;
+ const auto config_old = enabled_vendors_old.find(bundle_name);
+ if (config_old == enabled_vendors_old.end() || config_old->second.find(model.id) == config_old->second.end()) {
+ // if preferred printer model has SLA printer technology it's important to check the model for multi-part state
+ if (pt == ptSLA && suppress_sla_printer)
+ continue;
return pt;
+ }
+
+ if (const auto model_it_old = config_old->second.find(model.id);
+ model_it_old == config_old->second.end() || model_it_old->second != model_it->second) {
+ // if preferred printer model has SLA printer technology it's important to check the model for multi-part state
+ if (pt == ptSLA && suppress_sla_printer)
+ continue;
+ return pt;
+ }
}
}
}
@@ -2645,7 +2655,6 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
std::string preferred_model;
std::string preferred_variant;
- const auto enabled_vendors_old = app_config->vendors();
auto get_preferred_printer_model = [enabled_vendors, enabled_vendors_old, preferred_pt](const std::string& bundle_name, const Bundle& bundle, std::string& variant) {
const auto config = enabled_vendors.find(bundle_name);
if (config == enabled_vendors.end())
@@ -2901,8 +2910,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
}
p->any_sla_selected = p->check_sla_selected();
- if (p->only_sla_mode)
- p->any_fff_selected = p->check_fff_selected();
+ p->any_fff_selected = ! p->only_sla_mode && p->check_fff_selected();
p->update_materials(T_ANY);
if (!p->only_sla_mode)
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 5b0cf430c..3d914d5b6 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -1,6 +1,5 @@
#include "libslic3r/libslic3r.h"
#include "DoubleSlider.hpp"
-#include "DoubleSlider_Utils.hpp"
#include "libslic3r/GCode.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
@@ -27,7 +26,6 @@
#include <cmath>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
-#include <random>
#include "Field.hpp"
#include "format.hpp"
#include "NotificationManager.hpp"
@@ -2562,7 +2560,7 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int
if (mode == SingleExtruder && type == ColorChange && m_use_default_colors) {
#if 1
if (ticks.empty())
- return get_opposite_color((*m_colors)[0]);
+ return color_generator.get_opposite_color((*m_colors)[0]);
auto before_tick_it = std::lower_bound(ticks.begin(), ticks.end(), tick);
if (before_tick_it == ticks.end())
@@ -2571,24 +2569,24 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int
if (--before_tick_it; before_tick_it->type == ColorChange)
break;
if (before_tick_it->type == ColorChange)
- return get_opposite_color(before_tick_it->color);
- return get_opposite_color((*m_colors)[0]);
+ return color_generator.get_opposite_color(before_tick_it->color);
+ return color_generator.get_opposite_color((*m_colors)[0]);
}
if (before_tick_it == ticks.begin())
{
const std::string& frst_color = (*m_colors)[0];
if (before_tick_it->type == ColorChange)
- return get_opposite_color(frst_color, before_tick_it->color);
+ return color_generator.get_opposite_color(frst_color, before_tick_it->color);
auto next_tick_it = before_tick_it;
while (next_tick_it != ticks.end())
if (++next_tick_it; next_tick_it->type == ColorChange)
break;
if (next_tick_it->type == ColorChange)
- return get_opposite_color(frst_color, next_tick_it->color);
+ return color_generator.get_opposite_color(frst_color, next_tick_it->color);
- return get_opposite_color(frst_color);
+ return color_generator.get_opposite_color(frst_color);
}
std::string frst_color = "";
@@ -2609,13 +2607,13 @@ std::string TickCodeInfo::get_color_for_tick(TickCode tick, Type type, const int
if (before_tick_it->type == ColorChange) {
if (frst_color.empty())
- return get_opposite_color(before_tick_it->color);
- return get_opposite_color(before_tick_it->color, frst_color);
+ return color_generator.get_opposite_color(before_tick_it->color);
+ return color_generator.get_opposite_color(before_tick_it->color, frst_color);
}
if (frst_color.empty())
- return get_opposite_color((*m_colors)[0]);
- return get_opposite_color((*m_colors)[0], frst_color);
+ return color_generator.get_opposite_color((*m_colors)[0]);
+ return color_generator.get_opposite_color((*m_colors)[0], frst_color);
#else
const std::vector<std::string>& colors = ColorPrintColors::get();
if (ticks.empty())
diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp
index e0f713d87..23275cf2a 100644
--- a/src/slic3r/GUI/DoubleSlider.hpp
+++ b/src/slic3r/GUI/DoubleSlider.hpp
@@ -3,6 +3,7 @@
#include "libslic3r/CustomGCode.hpp"
#include "wxExtensions.hpp"
+#include "DoubleSlider_Utils.hpp"
#include <wx/window.h>
#include <wx/control.h>
@@ -118,6 +119,7 @@ class TickCodeInfo
// int m_default_color_idx = 0;
std::vector<std::string>* m_colors {nullptr};
+ ColorGenerator color_generator;
std::string get_color_for_tick(TickCode tick, Type type, const int extruder);
diff --git a/src/slic3r/GUI/DoubleSlider_Utils.hpp b/src/slic3r/GUI/DoubleSlider_Utils.hpp
index 704eb5a87..b5955f2fc 100644
--- a/src/slic3r/GUI/DoubleSlider_Utils.hpp
+++ b/src/slic3r/GUI/DoubleSlider_Utils.hpp
@@ -1,173 +1,191 @@
#include <stdio.h>
-#include "wx/colour.h"
-
-// next code is borrowed from https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
-
-typedef struct {
- double r; // a fraction between 0 and 1
- double g; // a fraction between 0 and 1
- double b; // a fraction between 0 and 1
-} rgb;
-
-typedef struct {
- double h; // angle in degrees
- double s; // a fraction between 0 and 1
- double v; // a fraction between 0 and 1
-} hsv;
+#include <random>
-static hsv rgb2hsv(rgb in);
-static rgb hsv2rgb(hsv in);
+#include "wx/colour.h"
-hsv rgb2hsv(rgb in)
+class ColorGenerator
{
- hsv out;
- double min, max, delta;
-
- min = in.r < in.g ? in.r : in.g;
- min = min < in.b ? min : in.b;
+ // Some of next code is borrowed from https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both
+ typedef struct {
+ double r; // a fraction between 0 and 1
+ double g; // a fraction between 0 and 1
+ double b; // a fraction between 0 and 1
+ } rgb;
+
+ typedef struct {
+ double h; // angle in degrees
+ double s; // a fraction between 0 and 1
+ double v; // a fraction between 0 and 1
+ } hsv;
+
+ //static hsv rgb2hsv(rgb in);
+ //static rgb hsv2rgb(hsv in);
+
+ hsv rgb2hsv(rgb in)
+ {
+ hsv out;
+ double min, max, delta;
+
+ min = in.r < in.g ? in.r : in.g;
+ min = min < in.b ? min : in.b;
+
+ max = in.r > in.g ? in.r : in.g;
+ max = max > in.b ? max : in.b;
+
+ out.v = max; // v
+ delta = max - min;
+ if (delta < 0.00001)
+ {
+ out.s = 0;
+ out.h = 0; // undefined, maybe nan?
+ return out;
+ }
+ if (max > 0.0) { // NOTE: if Max is == 0, this divide would cause a crash
+ out.s = (delta / max); // s
+ }
+ else {
+ // if max is 0, then r = g = b = 0
+ // s = 0, h is undefined
+ out.s = 0.0;
+ out.h = NAN; // its now undefined
+ return out;
+ }
+ if (in.r >= max) // > is bogus, just keeps compilor happy
+ out.h = (in.g - in.b) / delta; // between yellow & magenta
+ else
+ if (in.g >= max)
+ out.h = 2.0 + (in.b - in.r) / delta; // between cyan & yellow
+ else
+ out.h = 4.0 + (in.r - in.g) / delta; // between magenta & cyan
+
+ out.h *= 60.0; // degrees
+
+ if (out.h < 0.0)
+ out.h += 360.0;
- max = in.r > in.g ? in.r : in.g;
- max = max > in.b ? max : in.b;
+ return out;
+ }
- out.v = max; // v
- delta = max - min;
- if (delta < 0.00001)
+ hsv rgb2hsv(const std::string& str_clr_in)
{
- out.s = 0;
- out.h = 0; // undefined, maybe nan?
- return out;
+ wxColour clr(str_clr_in);
+ rgb in = { clr.Red() / 255.0, clr.Green() / 255.0, clr.Blue() / 255.0 };
+ return rgb2hsv(in);
}
- if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
- out.s = (delta / max); // s
- } else {
- // if max is 0, then r = g = b = 0
- // s = 0, h is undefined
- out.s = 0.0;
- out.h = NAN; // its now undefined
+
+
+ rgb hsv2rgb(hsv in)
+ {
+ double hh, p, q, t, ff;
+ long i;
+ rgb out;
+
+ if (in.s <= 0.0) { // < is bogus, just shuts up warnings
+ out.r = in.v;
+ out.g = in.v;
+ out.b = in.v;
+ return out;
+ }
+ hh = in.h;
+ if (hh >= 360.0) hh -= 360.0;//hh = 0.0;
+ hh /= 60.0;
+ i = (long)hh;
+ ff = hh - i;
+ p = in.v * (1.0 - in.s);
+ q = in.v * (1.0 - (in.s * ff));
+ t = in.v * (1.0 - (in.s * (1.0 - ff)));
+
+ switch (i) {
+ case 0:
+ out.r = in.v;
+ out.g = t;
+ out.b = p;
+ break;
+ case 1:
+ out.r = q;
+ out.g = in.v;
+ out.b = p;
+ break;
+ case 2:
+ out.r = p;
+ out.g = in.v;
+ out.b = t;
+ break;
+
+ case 3:
+ out.r = p;
+ out.g = q;
+ out.b = in.v;
+ break;
+ case 4:
+ out.r = t;
+ out.g = p;
+ out.b = in.v;
+ break;
+ case 5:
+ default:
+ out.r = in.v;
+ out.g = p;
+ out.b = q;
+ break;
+ }
return out;
}
- if( in.r >= max ) // > is bogus, just keeps compilor happy
- out.h = ( in.g - in.b ) / delta; // between yellow & magenta
- else
- if( in.g >= max )
- out.h = 2.0 + ( in.b - in.r ) / delta; // between cyan & yellow
- else
- out.h = 4.0 + ( in.r - in.g ) / delta; // between magenta & cyan
- out.h *= 60.0; // degrees
+ std::random_device rd;
- if( out.h < 0.0 )
- out.h += 360.0;
+public:
- return out;
-}
-
-hsv rgb2hsv(const std::string& str_clr_in)
-{
- wxColour clr(str_clr_in);
- rgb in = { clr.Red() / 255.0, clr.Green() / 255.0, clr.Blue() / 255.0 };
- return rgb2hsv(in);
-}
+ ColorGenerator() {}
+ ~ColorGenerator() {}
+ double rand_val()
+ {
+ std::mt19937 rand_generator(rd());
-rgb hsv2rgb(hsv in)
-{
- double hh, p, q, t, ff;
- long i;
- rgb out;
-
- if(in.s <= 0.0) { // < is bogus, just shuts up warnings
- out.r = in.v;
- out.g = in.v;
- out.b = in.v;
- return out;
- }
- hh = in.h;
- if (hh >= 360.0) hh -= 360.0;//hh = 0.0;
- hh /= 60.0;
- i = (long)hh;
- ff = hh - i;
- p = in.v * (1.0 - in.s);
- q = in.v * (1.0 - (in.s * ff));
- t = in.v * (1.0 - (in.s * (1.0 - ff)));
-
- switch(i) {
- case 0:
- out.r = in.v;
- out.g = t;
- out.b = p;
- break;
- case 1:
- out.r = q;
- out.g = in.v;
- out.b = p;
- break;
- case 2:
- out.r = p;
- out.g = in.v;
- out.b = t;
- break;
-
- case 3:
- out.r = p;
- out.g = q;
- out.b = in.v;
- break;
- case 4:
- out.r = t;
- out.g = p;
- out.b = in.v;
- break;
- case 5:
- default:
- out.r = in.v;
- out.g = p;
- out.b = q;
- break;
+ // this value will be used for Saturation and Value
+ // to avoid extremely light/dark colors, take this value from range [0.65; 1.0]
+ std::uniform_real_distribution<double> distrib(0.65, 1.0);
+ return distrib(rand_generator);
}
- return out;
-}
-double rand_val()
-{
- return 0.1 * (10 - rand() % 8);
-}
-std::string get_opposite_color(const std::string& color)
-{
- std::string opp_color = "";
+ std::string get_opposite_color(const std::string& color)
+ {
+ std::string opp_color = "";
- hsv hsv_clr = rgb2hsv(color);
- hsv_clr.h += 65; // 65 instead 60 to avoid circle values
- hsv_clr.s = rand_val();
- hsv_clr.v = rand_val();
+ hsv hsv_clr = rgb2hsv(color);
+ hsv_clr.h += 65; // 65 instead 60 to avoid circle values
+ hsv_clr.s = rand_val();
+ hsv_clr.v = rand_val();
- rgb rgb_opp_color = hsv2rgb(hsv_clr);
+ rgb rgb_opp_color = hsv2rgb(hsv_clr);
- wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
- opp_color = clr_str.ToStdString();
+ wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
+ opp_color = clr_str.ToStdString();
- return opp_color;
-}
+ return opp_color;
+ }
-std::string get_opposite_color(const std::string& color_frst, const std::string& color_scnd)
-{
- std::string opp_color = "";
+ std::string get_opposite_color(const std::string& color_frst, const std::string& color_scnd)
+ {
+ std::string opp_color = "";
- hsv hsv_frst = rgb2hsv(color_frst);
- hsv hsv_scnd = rgb2hsv(color_scnd);
+ hsv hsv_frst = rgb2hsv(color_frst);
+ hsv hsv_scnd = rgb2hsv(color_scnd);
- double delta_h = fabs(hsv_frst.h - hsv_scnd.h);
- double start_h = delta_h > 180 ? std::min<double>(hsv_scnd.h, hsv_frst.h) : std::max<double>(hsv_scnd.h, hsv_frst.h);
- start_h += 5; // to avoid circle change of colors for 120 deg
- if (delta_h < 180)
- delta_h = 360 - delta_h;
+ double delta_h = fabs(hsv_frst.h - hsv_scnd.h);
+ double start_h = delta_h > 180 ? std::min<double>(hsv_scnd.h, hsv_frst.h) : std::max<double>(hsv_scnd.h, hsv_frst.h);
+ start_h += 5; // to avoid circle change of colors for 120 deg
+ if (delta_h < 180)
+ delta_h = 360 - delta_h;
- hsv hsv_opp = hsv{ start_h + 0.5 * delta_h, rand_val(), rand_val() };
- rgb rgb_opp_color = hsv2rgb(hsv_opp);
+ hsv hsv_opp = hsv{ start_h + 0.5 * delta_h, rand_val(), rand_val() };
+ rgb rgb_opp_color = hsv2rgb(hsv_opp);
- wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
- opp_color = clr_str.ToStdString();
+ wxString clr_str = wxString::Format(wxT("#%02X%02X%02X"), (unsigned char)(rgb_opp_color.r * 255), (unsigned char)(rgb_opp_color.g * 255), (unsigned char)(rgb_opp_color.b * 255));
+ opp_color = clr_str.ToStdString();
- return opp_color;
-} \ No newline at end of file
+ return opp_color;
+ }
+}; \ No newline at end of file
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 6b2703077..3d16c7244 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -73,9 +73,19 @@ static std::vector<std::array<float, 4>> decode_colors(const std::vector<std::st
return output;
}
-static float round_to_nearest_percent(float value)
+// Round to a bin with minimum two digits resolution.
+// Equivalent to conversion to string with sprintf(buf, "%.2g", value) and conversion back to float, but faster.
+static float round_to_bin(const float value)
{
- return std::round(value * 100.f) * 0.01f;
+// assert(value > 0);
+ constexpr float const scale [5] = { 100.f, 1000.f, 10000.f, 100000.f, 1000000.f };
+ constexpr float const invscale [5] = { 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f };
+ constexpr float const threshold[5] = { 0.095f, 0.0095f, 0.00095f, 0.000095f, 0.0000095f };
+ // Scaling factor, pointer to the tables above.
+ int i = 0;
+ // While the scaling factor is not yet large enough to get two integer digits after scaling and rounding:
+ for (; value < threshold[i] && i < 4; ++ i) ;
+ return std::round(value * scale[i]) * invscale[i];
}
void GCodeViewer::VBuffer::reset()
@@ -138,7 +148,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessorResult::MoveVertex& move) co
// use rounding to reduce the number of generated paths
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
- height == round_to_nearest_percent(move.height) && width == round_to_nearest_percent(move.width) &&
+ height == round_to_bin(move.height) && width == round_to_bin(move.width) &&
matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
}
case EMoveType::Travel: {
@@ -171,7 +181,7 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessorResult::MoveVertex& move
Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
// use rounding to reduce the number of generated paths
paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
- round_to_nearest_percent(move.height), round_to_nearest_percent(move.width),
+ round_to_bin(move.height), round_to_bin(move.width),
move.feedrate, move.fan_speed, move.temperature,
move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
}
@@ -608,14 +618,14 @@ void GCodeViewer::init()
case EMoveType::Retract:
case EMoveType::Unretract:
case EMoveType::Seam: {
- if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
- buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
- buffer.shader = "gouraud_light_instanced";
- buffer.model.model.init_from(diamond(16));
- buffer.model.color = option_color(type);
- buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
- }
- else {
+// if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
+// buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
+// buffer.shader = "gouraud_light_instanced";
+// buffer.model.model.init_from(diamond(16));
+// buffer.model.color = option_color(type);
+// buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
+// }
+// else {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
buffer.shader = "gouraud_light";
@@ -623,7 +633,7 @@ void GCodeViewer::init()
buffer.model.data = diamond(16);
buffer.model.color = option_color(type);
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
- }
+// }
break;
}
case EMoveType::Wipe:
@@ -635,7 +645,7 @@ void GCodeViewer::init()
}
case EMoveType::Travel: {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
- buffer.vertices.format = VBuffer::EFormat::PositionNormal1;
+ buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
buffer.shader = "toolpaths_lines";
break;
}
@@ -773,12 +783,12 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v
{
case EMoveType::Extrude:
{
- m_extrusions.ranges.height.update_from(round_to_nearest_percent(curr.height));
- m_extrusions.ranges.width.update_from(round_to_nearest_percent(curr.width));
+ m_extrusions.ranges.height.update_from(round_to_bin(curr.height));
+ m_extrusions.ranges.width.update_from(round_to_bin(curr.width));
m_extrusions.ranges.fan_speed.update_from(curr.fan_speed);
m_extrusions.ranges.temperature.update_from(curr.temperature);
if (curr.extrusion_role != erCustom || is_visible(erCustom))
- m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest_percent(curr.volumetric_rate()));
+ m_extrusions.ranges.volumetric_rate.update_from(round_to_bin(curr.volumetric_rate()));
[[fallthrough]];
}
case EMoveType::Travel:
@@ -1197,15 +1207,19 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
// format data into the buffers to be rendered as lines
auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
// x component of the normal to the current segment (the normal is parallel to the XY plane)
- const float normal_x = (curr.position - prev.position).normalized().y();
+ const Vec3f dir = (curr.position - prev.position).normalized();
+ Vec3f normal(dir.y(), -dir.x(), 0.0);
+ normal.normalize();
- auto add_vertex = [&vertices, normal_x](const GCodeProcessorResult::MoveVertex& vertex) {
+ auto add_vertex = [&vertices, &normal](const GCodeProcessorResult::MoveVertex& vertex) {
// add position
vertices.push_back(vertex.position.x());
vertices.push_back(vertex.position.y());
vertices.push_back(vertex.position.z());
- // add normal x component
- vertices.push_back(normal_x);
+ // add normal
+ vertices.push_back(normal.x());
+ vertices.push_back(normal.y());
+ vertices.push_back(normal.z());
};
// add previous vertex
@@ -2731,7 +2745,10 @@ void GCodeViewer::render_toolpaths()
for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) {
const RenderPath& path = *it;
- glsafe(::glUniform4fv(uniform_color, 1, static_cast<const GLfloat*>(path.color.data())));
+ // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415.
+ assert(! path.sizes.empty());
+ assert(! path.offsets.empty());
+ shader.set_uniform(uniform_color, path.color);
glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.gl_multi_points_calls_count;
@@ -2752,7 +2769,10 @@ void GCodeViewer::render_toolpaths()
](std::vector<RenderPath>::iterator it_path, std::vector<RenderPath>::iterator it_end, GLShaderProgram& shader, int uniform_color) {
for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) {
const RenderPath& path = *it;
- glsafe(::glUniform4fv(uniform_color, 1, static_cast<const GLfloat*>(path.color.data())));
+ // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415.
+ assert(! path.sizes.empty());
+ assert(! path.offsets.empty());
+ shader.set_uniform(uniform_color, path.color);
glsafe(::glMultiDrawElements(GL_LINES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.gl_multi_lines_calls_count;
@@ -2767,7 +2787,10 @@ void GCodeViewer::render_toolpaths()
](std::vector<RenderPath>::iterator it_path, std::vector<RenderPath>::iterator it_end, GLShaderProgram& shader, int uniform_color) {
for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) {
const RenderPath& path = *it;
- glsafe(::glUniform4fv(uniform_color, 1, static_cast<const GLfloat*>(path.color.data())));
+ // Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415.
+ assert(! path.sizes.empty());
+ assert(! path.offsets.empty());
+ shader.set_uniform(uniform_color, path.color);
glsafe(::glMultiDrawElements(GL_TRIANGLES, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.gl_multi_triangles_calls_count;
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 5afab366a..b03206090 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1869,6 +1869,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// updates volumes transformations
volume->set_instance_transformation(mvs->model_volume->get_object()->instances[mvs->composite_id.instance_id]->get_transformation());
volume->set_volume_transformation(mvs->model_volume->get_transformation());
+
+ // updates volumes convex hull
+ if (mvs->model_volume->is_model_part() && ! volume->convex_hull())
+ // Model volume was likely changed from modifier or support blocker / enforcer to a model part.
+ // Only model parts require convex hulls.
+ volume->set_convex_hull(mvs->model_volume->get_convex_hull_shared_ptr());
}
}
}
@@ -4020,8 +4026,11 @@ bool GLCanvas3D::_render_search_list(float pos_x)
action_taken = true;
else
sidebar.jump_to_option(selected);*/
- if (selected != 9999)
+ if (selected != 9999) {
+ imgui->end(); // end imgui before the jump to option
sidebar.jump_to_option(selected);
+ return true;
+ }
action_taken = true;
}
diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp
index 9c1e93652..4c36efdd9 100644
--- a/src/slic3r/GUI/GLShader.cpp
+++ b/src/slic3r/GUI/GLShader.cpp
@@ -206,154 +206,104 @@ void GLShaderProgram::stop_using() const
glsafe(::glUseProgram(0));
}
-bool GLShaderProgram::set_uniform(const char* name, int value) const
+void GLShaderProgram::set_uniform(int id, int value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
- glsafe(::glUniform1i(id, static_cast<GLint>(value)));
- return true;
- }
- return false;
+ if (id >= 0)
+ glsafe(::glUniform1i(id, value));
}
-bool GLShaderProgram::set_uniform(const char* name, bool value) const
+void GLShaderProgram::set_uniform(int id, bool value) const
{
- return set_uniform(name, value ? 1 : 0);
+ set_uniform(id, value ? 1 : 0);
}
-bool GLShaderProgram::set_uniform(const char* name, float value) const
+void GLShaderProgram::set_uniform(int id, float value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
- glsafe(::glUniform1f(id, static_cast<GLfloat>(value)));
- return true;
- }
- return false;
+ if (id >= 0)
+ glsafe(::glUniform1f(id, value));
}
-bool GLShaderProgram::set_uniform(const char* name, double value) const
+void GLShaderProgram::set_uniform(int id, double value) const
{
- return set_uniform(name, static_cast<float>(value));
+ set_uniform(id, static_cast<float>(value));
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 2>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<int, 2>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform2iv(id, 1, static_cast<const GLint*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 3>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<int, 3>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform3iv(id, 1, static_cast<const GLint*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<int, 4>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<int, 4>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform4iv(id, 1, static_cast<const GLint*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<float, 2>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 3>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<float, 3>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 4>& value) const
+void GLShaderProgram::set_uniform(int id, const std::array<float, 4>& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const float* value, size_t size) const
+void GLShaderProgram::set_uniform(int id, const float* value, size_t size) const
{
- if (size == 1)
- return set_uniform(name, value[0]);
- else if (size < 5) {
- int id = get_uniform_location(name);
- if (id >= 0) {
- if (size == 2)
- glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
- else if (size == 3)
- glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
- else
- glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
-
- return true;
- }
+ if (id >= 0) {
+ if (size == 1)
+ set_uniform(id, value[0]);
+ else if (size == 2)
+ glsafe(::glUniform2fv(id, 1, static_cast<const GLfloat*>(value)));
+ else if (size == 3)
+ glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value)));
+ else if (size == 4)
+ glsafe(::glUniform4fv(id, 1, static_cast<const GLfloat*>(value)));
}
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const Transform3f& value) const
+void GLShaderProgram::set_uniform(int id, const Transform3f& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniformMatrix4fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.matrix().data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const Transform3d& value) const
+void GLShaderProgram::set_uniform(int id, const Transform3d& value) const
{
- return set_uniform(name, value.cast<float>());
+ set_uniform(id, value.cast<float>());
}
-bool GLShaderProgram::set_uniform(const char* name, const Matrix3f& value) const
+void GLShaderProgram::set_uniform(int id, const Matrix3f& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniformMatrix3fv(id, 1, GL_FALSE, static_cast<const GLfloat*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const Vec3f& value) const
+void GLShaderProgram::set_uniform(int id, const Vec3f& value) const
{
- int id = get_uniform_location(name);
- if (id >= 0) {
+ if (id >= 0)
glsafe(::glUniform3fv(id, 1, static_cast<const GLfloat*>(value.data())));
- return true;
- }
- return false;
}
-bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const
+void GLShaderProgram::set_uniform(int id, const Vec3d& value) const
{
- return set_uniform(name, static_cast<Vec3f>(value.cast<float>()));
+ set_uniform(id, static_cast<Vec3f>(value.cast<float>()));
}
int GLShaderProgram::get_attrib_location(const char* name) const
diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp
index d7b92000d..46e46b4f0 100644
--- a/src/slic3r/GUI/GLShader.hpp
+++ b/src/slic3r/GUI/GLShader.hpp
@@ -44,22 +44,39 @@ public:
void start_using() const;
void stop_using() const;
- bool set_uniform(const char* name, int value) const;
- bool set_uniform(const char* name, bool value) const;
- bool set_uniform(const char* name, float value) const;
- bool set_uniform(const char* name, double value) const;
- bool set_uniform(const char* name, const std::array<int, 2>& value) const;
- bool set_uniform(const char* name, const std::array<int, 3>& value) const;
- bool set_uniform(const char* name, const std::array<int, 4>& value) const;
- bool set_uniform(const char* name, const std::array<float, 2>& value) const;
- bool set_uniform(const char* name, const std::array<float, 3>& value) const;
- bool set_uniform(const char* name, const std::array<float, 4>& value) const;
- bool set_uniform(const char* name, const float* value, size_t size) const;
- bool set_uniform(const char* name, const Transform3f& value) const;
- bool set_uniform(const char* name, const Transform3d& value) const;
- bool set_uniform(const char* name, const Matrix3f& value) const;
- bool set_uniform(const char* name, const Vec3f& value) const;
- bool set_uniform(const char* name, const Vec3d& value) const;
+ void set_uniform(const char* name, int value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, bool value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, float value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, double value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<int, 2>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<int, 3>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<int, 4>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<float, 2>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<float, 3>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const std::array<float, 4>& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const float* value, size_t size) const { set_uniform(get_uniform_location(name), value, size); }
+ void set_uniform(const char* name, const Transform3f& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const Transform3d& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const Matrix3f& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const Vec3f& value) const { set_uniform(get_uniform_location(name), value); }
+ void set_uniform(const char* name, const Vec3d& value) const { set_uniform(get_uniform_location(name), value); }
+
+ void set_uniform(int id, int value) const;
+ void set_uniform(int id, bool value) const;
+ void set_uniform(int id, float value) const;
+ void set_uniform(int id, double value) const;
+ void set_uniform(int id, const std::array<int, 2>& value) const;
+ void set_uniform(int id, const std::array<int, 3>& value) const;
+ void set_uniform(int id, const std::array<int, 4>& value) const;
+ void set_uniform(int id, const std::array<float, 2>& value) const;
+ void set_uniform(int id, const std::array<float, 3>& value) const;
+ void set_uniform(int id, const std::array<float, 4>& value) const;
+ void set_uniform(int id, const float* value, size_t size) const;
+ void set_uniform(int id, const Transform3f& value) const;
+ void set_uniform(int id, const Transform3d& value) const;
+ void set_uniform(int id, const Matrix3f& value) const;
+ void set_uniform(int id, const Vec3f& value) const;
+ void set_uniform(int id, const Vec3d& value) const;
// returns -1 if not found
int get_attrib_location(const char* name) const;
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 8fe951292..fb7fa00f1 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -352,7 +352,7 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su
add_config_substitutions(substitution.substitutions, changes);
}
- InfoDialog msg(nullptr, _L("Configuration bundle was loaded, however some configuration values were not recognized."), substitution_message(changes));
+ InfoDialog msg(nullptr, _L("Configuration bundle was loaded, however some configuration values were not recognized."), substitution_message(changes), true);
msg.ShowModal();
}
@@ -363,7 +363,7 @@ void show_substitutions_info(const ConfigSubstitutions& config_substitutions, co
InfoDialog msg(nullptr,
format_wxstr(_L("Configuration file \"%1%\" was loaded, however some configuration values were not recognized."), from_u8(filename)),
- substitution_message(changes));
+ substitution_message(changes), true);
msg.ShowModal();
}
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index bda7394ed..5ebd5300a 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -862,9 +862,9 @@ static boost::optional<Semver> parse_semver_from_ini(std::string path)
void GUI_App::init_app_config()
{
// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
-// SetAppName(SLIC3R_APP_KEY);
+ SetAppName(SLIC3R_APP_KEY);
// SetAppName(SLIC3R_APP_KEY "-alpha");
- SetAppName(SLIC3R_APP_KEY "-beta");
+// SetAppName(SLIC3R_APP_KEY "-beta");
// SetAppDisplayName(SLIC3R_APP_NAME);
// Set the Slic3r data directory at the Slic3r XS module.
@@ -909,17 +909,18 @@ void GUI_App::init_app_config()
"\n\n" + app_config->config_path() + "\n\n" + error);
}
}
- // Save orig_version here, so its empty if no app_config existed before this run.
- m_last_config_version = app_config->orig_version();//parse_semver_from_ini(app_config->config_path());
}
}
-// returns true if found newer version and user agreed to use it
-bool GUI_App::check_older_app_config(Semver current_version, bool backup)
+// returns old config path to copy from if such exists,
+// returns an empty string if such config path does not exists or if it cannot be loaded.
+std::string GUI_App::check_older_app_config(Semver current_version, bool backup)
{
+ std::string older_data_dir_path;
+
// If the config folder is redefined - do not check
if (m_datadir_redefined)
- return false;
+ return {};
// find other version app config (alpha / beta / release)
std::string config_path = app_config->config_path();
@@ -940,55 +941,57 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
boost::optional<Semver>other_semver = parse_semver_from_ini(candidate.string());
if (other_semver && *other_semver > last_semver) {
last_semver = *other_semver;
- m_older_data_dir_path = candidate.parent_path().string();
+ older_data_dir_path = candidate.parent_path().string();
}
}
}
- if (m_older_data_dir_path.empty())
- return false;
- BOOST_LOG_TRIVIAL(info) << "last app config file used: " << m_older_data_dir_path;
+ if (older_data_dir_path.empty())
+ return {};
+ BOOST_LOG_TRIVIAL(info) << "last app config file used: " << older_data_dir_path;
// ask about using older data folder
- RichMessageDialog msg(nullptr, backup ?
- wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
- "\nIts version is %s."
- "\nLast version you used in current configuration folder is %s."
- "\nPlease note that PrusaSlicer uses different folders to save configuration of alpha, beta and full release versions."
- "\nWould you like to copy found configuration to your current configuration folder?"
-
- "\n\nIf you select yes, PrusaSlicer will copy all profiles and other files from found folder to the current one. Overwriting any existing file with matching name."
- "\nIf you select no, you will continue with current configuration.")
- , m_older_data_dir_path, last_semver.to_string(), current_version.to_string())
- : wxString::Format(_L("PrusaSlicer detected another configuration folder at %s."
- "\nIts version is %s."
- "\nThere is no configuration file in current configuration folder."
- "\nPlease note that PrusaSlicer uses different folders to save configuration of alpha, beta and full release versions."
- "\nWould you like to copy found configuration to your current configuration folder?"
-
- "\n\nIf you select yes, PrusaSlicer will copy all profiles and other files from found folder to the current one."
- "\nIf you select no, you will start with clean installation with configuration wizard.")
- , m_older_data_dir_path, last_semver.to_string())
- , _L("PrusaSlicer"), /*wxICON_QUESTION | */wxYES_NO);
+
+ InfoDialog msg(nullptr
+ , format_wxstr(_L("You are opening %1% version %2%."), SLIC3R_APP_NAME, SLIC3R_VERSION)
+ , backup ?
+ format_wxstr(_L(
+ "The active configuration was created by <b>%1% %2%</b>,"
+ "\nwhile a newer configuration was found in <b>%3%</b>"
+ "\ncreated by <b>%1% %4%</b>."
+ "\n\nShall the newer configuration be imported?"
+ "\nIf so, your active configuration will be backed up before importing the new configuration."
+ )
+ , SLIC3R_APP_NAME, current_version.to_string(), older_data_dir_path, last_semver.to_string())
+ : format_wxstr(_L(
+ "An existing configuration was found in <b>%3%</b>"
+ "\ncreated by <b>%1% %2%</b>."
+ "\n\nShall this configuration be imported?"
+ )
+ , SLIC3R_APP_NAME, last_semver.to_string(), older_data_dir_path)
+ , true, wxYES_NO);
+
+ if (backup) {
+ msg.SetButtonLabel(wxID_YES, _L("Import"));
+ msg.SetButtonLabel(wxID_NO, _L("Don't import"));
+ }
+
if (msg.ShowModal() == wxID_YES) {
std::string snapshot_id;
if (backup) {
- // configuration snapshot
- std::string comment;
- if (const Config::Snapshot* snapshot = Config::take_config_snapshot_report_error(
- *app_config,
- Config::Snapshot::SNAPSHOT_USER,
- comment);
- snapshot != nullptr)
- // Is thos correct? Save snapshot id for later, when new app config is loaded.
+ const Config::Snapshot* snapshot{ nullptr };
+ if (! GUI::Config::take_config_snapshot_cancel_on_error(*app_config, Config::Snapshot::SNAPSHOT_USER, "",
+ _u8L("Continue and import newer configuration?"), &snapshot))
+ return {};
+ if (snapshot) {
+ // Save snapshot ID before loading the alternate AppConfig, as loading the alternate AppConfig may fail.
snapshot_id = snapshot->id;
- else
- BOOST_LOG_TRIVIAL(error) << "Failed to take congiguration snapshot: ";
+ assert(! snapshot_id.empty());
+ app_config->set("on_snapshot", snapshot_id);
+ } else
+ BOOST_LOG_TRIVIAL(error) << "Failed to take congiguration snapshot";
}
- // This will tell later (when config folder structure is sure to exists) to copy files from m_older_data_dir_path
- m_init_app_config_from_older = true;
// load app config from older file
- app_config->set_loading_path((boost::filesystem::path(m_older_data_dir_path) / filename).string());
- std::string error = app_config->load();
+ std::string error = app_config->load((boost::filesystem::path(older_data_dir_path) / filename).string());
if (!error.empty()) {
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
if (is_editor()) {
@@ -1007,14 +1010,9 @@ bool GUI_App::check_older_app_config(Semver current_version, bool backup)
if (!snapshot_id.empty())
app_config->set("on_snapshot", snapshot_id);
m_app_conf_exists = true;
- return true;
+ return older_data_dir_path;
}
- return false;
-}
-
-void GUI_App::copy_older_config()
-{
- preset_bundle->copy_files(m_older_data_dir_path);
+ return {};
}
void GUI_App::init_single_instance_checker(const std::string &name, const std::string &path)
@@ -1035,6 +1033,9 @@ bool GUI_App::OnInit()
bool GUI_App::on_init_inner()
{
+ // Set initialization of image handlers before any UI actions - See GH issue #7469
+ wxInitAllImageHandlers();
+
#if defined(_WIN32) && ! defined(_WIN64)
// Win32 32bit build.
if (wxPlatformInfo::Get().GetArchName().substr(0, 2) == "64") {
@@ -1077,6 +1078,42 @@ bool GUI_App::on_init_inner()
// Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
+ // !!! Initialization of UI settings as a language, application color mode, fonts... have to be done before first UI action.
+ // Like here, before the show InfoDialog in check_older_app_config()
+
+ // If load_language() fails, the application closes.
+ load_language(wxString(), true);
+#ifdef _MSW_DARK_MODE
+ bool init_dark_color_mode = app_config->get("dark_color_mode") == "1";
+ bool init_sys_menu_enabled = app_config->get("sys_menu_enabled") == "1";
+ NppDarkMode::InitDarkMode(init_dark_color_mode, init_sys_menu_enabled);
+#endif
+ // initialize label colors and fonts
+ init_label_colours();
+ init_fonts();
+
+ std::string older_data_dir_path;
+ if (m_app_conf_exists) {
+ if (app_config->orig_version().valid() && app_config->orig_version() < *Semver::parse(SLIC3R_VERSION))
+ // Only copying configuration if it was saved with a newer slicer than the one currently running.
+ older_data_dir_path = check_older_app_config(app_config->orig_version(), true);
+ } else {
+ // No AppConfig exists, fresh install. Always try to copy from an alternate location, don't make backup of the current configuration.
+ older_data_dir_path = check_older_app_config(Semver(), false);
+ }
+
+#ifdef _MSW_DARK_MODE
+ // app_config can be updated in check_older_app_config(), so check if dark_color_mode and sys_menu_enabled was changed
+ if (bool new_dark_color_mode = app_config->get("dark_color_mode") == "1";
+ init_dark_color_mode != new_dark_color_mode) {
+ NppDarkMode::SetDarkMode(new_dark_color_mode);
+ init_label_colours();
+ update_label_colours_from_appconfig();
+ }
+ if (bool new_sys_menu_enabled = app_config->get("sys_menu_enabled") == "1";
+ init_sys_menu_enabled != new_sys_menu_enabled)
+ NppDarkMode::SetSystemMenuForApp(new_sys_menu_enabled);
+#endif
if (is_editor()) {
std::string msg = Http::tls_global_init();
@@ -1098,24 +1135,6 @@ bool GUI_App::on_init_inner()
}
}
- if (m_last_config_version) {
- if (*m_last_config_version < *Semver::parse(SLIC3R_VERSION))
- check_older_app_config(*m_last_config_version, true);
- } else {
- check_older_app_config(Semver(), false);
- }
-
- app_config->set("version", SLIC3R_VERSION);
- app_config->save();
-
- // If load_language() fails, the application closes.
- load_language(wxString(), true);
-
- wxInitAllImageHandlers();
-
-#ifdef _MSW_DARK_MODE
- NppDarkMode::InitDarkMode(app_config->get("dark_color_mode") == "1", app_config->get("sys_menu_enabled") == "1");
-#endif
SplashScreen* scrn = nullptr;
if (app_config->get("show_splash_screen") == "1") {
// make a bitmap with dark grey banner on the left side
@@ -1139,17 +1158,18 @@ bool GUI_App::on_init_inner()
scrn->SetText(_L("Loading configuration")+ dots);
}
-
-
preset_bundle = new PresetBundle();
// just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory
// supplied as argument to --datadir; in that case we should still run the wizard
preset_bundle->setup_directories();
-
- if (m_init_app_config_from_older)
- copy_older_config();
+ if (! older_data_dir_path.empty())
+ preset_bundle->import_newer_configs(older_data_dir_path);
+
+ // Save PrusaSlicer.ini after possibly copying the config from the alternate location and after all the configs from the alternate location were copied.
+ app_config->set("version", SLIC3R_VERSION);
+ app_config->save();
if (is_editor()) {
#ifdef __WXMSW__
@@ -1198,10 +1218,6 @@ bool GUI_App::on_init_inner()
#endif // __WXMSW__
}
- // initialize label colors and fonts
- init_label_colours();
- init_fonts();
-
// Suppress the '- default -' presets.
preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1");
try {
@@ -1372,24 +1388,41 @@ void GUI_App::update_label_colours()
tab->update_label_colours();
}
+static bool is_focused(HWND hWnd)
+{
+ HWND hFocusedWnd = ::GetFocus();
+ return hFocusedWnd && hWnd == hFocusedWnd;
+}
+
void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool just_font/* = false*/)
{
#ifdef _WIN32
+ bool is_focused_button = false;
if (wxButton* btn = dynamic_cast<wxButton*>(window)) {
if (!(btn->GetWindowStyle() & wxNO_BORDER)) {
btn->SetWindowStyle(btn->GetWindowStyle() | wxNO_BORDER);
highlited = true;
}
- // hovering for buttons
+ // button marking
{
- auto focus_button = [this, btn](const bool focus) {
- btn->SetForegroundColour(focus ? m_color_hovered_btn_label : m_color_label_default);
+ auto mark_button = [this, btn, highlited](const bool mark) {
+ if (btn->GetLabel().IsEmpty())
+ btn->SetBackgroundColour(mark ? m_color_selected_btn_bg : highlited ? m_color_highlight_default : m_color_window_default);
+ else
+ btn->SetForegroundColour(mark ? m_color_hovered_btn_label : m_color_label_default);
btn->Refresh();
btn->Update();
};
- btn->Bind(wxEVT_ENTER_WINDOW, [focus_button](wxMouseEvent& event) { focus_button(true); event.Skip(); });
- btn->Bind(wxEVT_LEAVE_WINDOW, [focus_button](wxMouseEvent& event) { focus_button(false); event.Skip(); });
+ // hovering
+ btn->Bind(wxEVT_ENTER_WINDOW, [mark_button](wxMouseEvent& event) { mark_button(true); event.Skip(); });
+ btn->Bind(wxEVT_LEAVE_WINDOW, [mark_button, btn](wxMouseEvent& event) { mark_button(is_focused(btn->GetHWND())); event.Skip(); });
+ // focusing
+ btn->Bind(wxEVT_SET_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(true); event.Skip(); });
+ btn->Bind(wxEVT_KILL_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(false); event.Skip(); });
+
+ if (is_focused_button = is_focused(btn->GetHWND()))
+ mark_button(true);
}
}
else if (wxTextCtrl* text = dynamic_cast<wxTextCtrl*>(window)) {
@@ -1411,7 +1444,8 @@ void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool ju
if (!just_font)
window->SetBackgroundColour(highlited ? m_color_highlight_default : m_color_window_default);
- window->SetForegroundColour(m_color_label_default);
+ if (!is_focused_button)
+ window->SetForegroundColour(m_color_label_default);
#endif
}
@@ -1702,6 +1736,7 @@ void GUI_App::force_colors_update()
if (WXHWND wxHWND = wxToolTip::GetToolTipCtrl())
NppDarkMode::SetDarkExplorerTheme((HWND)wxHWND);
NppDarkMode::SetDarkTitleBar(mainframe->GetHWND());
+ NppDarkMode::SetDarkTitleBar(mainframe->m_settings_dialog.GetHWND());
#endif //_MSW_DARK_MODE
m_force_colors_update = true;
}
@@ -1718,9 +1753,18 @@ void GUI_App::update_ui_from_settings()
m_force_colors_update = false;
mainframe->force_color_changed();
mainframe->diff_dialog.force_color_changed();
+ mainframe->preferences_dialog->force_color_changed();
mainframe->printhost_queue_dlg()->force_color_changed();
#ifdef _MSW_DARK_MODE
update_scrolls(mainframe);
+ if (mainframe->is_dlg_layout()) {
+ // update for tabs bar
+ UpdateDarkUI(&mainframe->m_settings_dialog);
+ mainframe->m_settings_dialog.Fit();
+ mainframe->m_settings_dialog.Refresh();
+ // update scrollbars
+ update_scrolls(&mainframe->m_settings_dialog);
+ }
#endif //_MSW_DARK_MODE
}
#endif
@@ -2162,9 +2206,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
local_menu->Append(config_id_base + ConfigMenuLanguage, _L("&Language"));
if (is_editor()) {
local_menu->AppendSeparator();
- local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _L("Flash printer &firmware"), _L("Upload a firmware image into an Arduino based printer"));
+ local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _L("Flash Printer &Firmware"), _L("Upload a firmware image into an Arduino based printer"));
// TODO: for when we're able to flash dictionaries
- // local_menu->Append(config_id_base + FirmwareMenuDict, _L("Flash language file"), _L("Upload a language dictionary file into a Prusa printer"));
+ // local_menu->Append(config_id_base + FirmwareMenuDict, _L("Flash Language File"), _L("Upload a language dictionary file into a Prusa printer"));
}
local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
@@ -2230,40 +2274,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
break;
case ConfigMenuPreferences:
{
- bool app_layout_changed = false;
- {
- // the dialog needs to be destroyed before the call to recreate_GUI()
- // or sometimes the application crashes into wxDialogBase() destructor
- // so we put it into an inner scope
- PreferencesDialog dlg(mainframe);
- dlg.ShowModal();
- app_layout_changed = dlg.settings_layout_changed();
- if (dlg.seq_top_layer_only_changed())
- this->plater_->refresh_print();
-
- if (dlg.recreate_GUI()) {
- recreate_GUI(_L("Restart application") + dots);
- return;
- }
-#ifdef _WIN32
- if (is_editor()) {
- if (app_config->get("associate_3mf") == "1")
- associate_3mf_files();
- if (app_config->get("associate_stl") == "1")
- associate_stl_files();
- }
- else {
- if (app_config->get("associate_gcode") == "1")
- associate_gcode_files();
- }
-#endif // _WIN32
- }
- if (app_layout_changed) {
- // hide full main_sizer for mainFrame
- mainframe->GetSizer()->Show(false);
- mainframe->update_layout();
- mainframe->select_tab(size_t(0));
- }
+ open_preferences();
break;
}
case ConfigMenuLanguage:
@@ -2311,36 +2322,34 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
menu->Append(local_menu, _L("&Configuration"));
}
-void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_option)
+void GUI_App::open_preferences(const std::string& highlight_option /*= std::string()*/, const std::string& tab_name/*= std::string()*/)
{
- bool app_layout_changed = false;
- {
- // the dialog needs to be destroyed before the call to recreate_GUI()
- // or sometimes the application crashes into wxDialogBase() destructor
- // so we put it into an inner scope
- PreferencesDialog dlg(mainframe, open_on_tab, highlight_option);
- dlg.ShowModal();
- app_layout_changed = dlg.settings_layout_changed();
+ mainframe->preferences_dialog->show(highlight_option, tab_name);
+
+ if (mainframe->preferences_dialog->recreate_GUI())
+ recreate_GUI(_L("Restart application") + dots);
+
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
- if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
+ if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
- if (dlg.seq_top_layer_only_changed())
+ if (mainframe->preferences_dialog->seq_top_layer_only_changed())
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
- this->plater_->refresh_print();
+ this->plater_->refresh_print();
+
#ifdef _WIN32
- if (is_editor()) {
- if (app_config->get("associate_3mf") == "1")
- associate_3mf_files();
- if (app_config->get("associate_stl") == "1")
- associate_stl_files();
- }
- else {
- if (app_config->get("associate_gcode") == "1")
- associate_gcode_files();
- }
-#endif // _WIN32
+ if (is_editor()) {
+ if (app_config->get("associate_3mf") == "1")
+ associate_3mf_files();
+ if (app_config->get("associate_stl") == "1")
+ associate_stl_files();
}
- if (app_layout_changed) {
+ else {
+ if (app_config->get("associate_gcode") == "1")
+ associate_gcode_files();
+ }
+#endif // _WIN32
+
+ if (mainframe->preferences_dialog->settings_layout_changed()) {
// hide full main_sizer for mainFrame
mainframe->GetSizer()->Show(false);
mainframe->update_layout();
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index b1b0a7786..eddaf4a97 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -264,7 +264,7 @@ public:
wxString current_language_code_safe() const;
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
- void open_preferences(size_t open_on_tab = 0, const std::string& highlight_option = std::string());
+ void open_preferences(const std::string& highlight_option = std::string(), const std::string& tab_name = std::string());
virtual bool OnExceptionInMainLoop() override;
// Calls wxLaunchDefaultBrowser if user confirms in dialog.
@@ -341,8 +341,9 @@ public:
private:
bool on_init_inner();
void init_app_config();
- bool check_older_app_config(Semver current_version, bool backup);
- void copy_older_config();
+ // returns old config path to copy from if such exists,
+ // returns an empty string if such config path does not exists or if it cannot be loaded.
+ std::string check_older_app_config(Semver current_version, bool backup);
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
void window_pos_sanitize(wxTopLevelWindow* window);
@@ -351,10 +352,7 @@ private:
bool config_wizard_startup();
void check_updates(const bool verbose);
- bool m_init_app_config_from_older { false };
- bool m_datadir_redefined { false };
- std::string m_older_data_dir_path;
- boost::optional<Semver> m_last_config_version;
+ bool m_datadir_redefined { false };
};
DECLARE_APP(GUI_App)
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index f4d27b0b2..a02abc849 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -349,7 +349,8 @@ void ObjectList::get_selection_indexes(std::vector<int>& obj_idxs, std::vector<i
{
wxDataViewItemArray sels;
GetSelections(sels);
- assert(!sels.IsEmpty());
+ if (sels.IsEmpty())
+ return;
if ( m_objects_model->GetItemType(sels[0]) & itVolume ||
(sels.Count()==1 && m_objects_model->GetItemType(m_objects_model->GetParent(sels[0])) & itVolume) ) {
@@ -424,7 +425,7 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol
if (!stats.manifold()) {
remaining_info = format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges);
- tooltip += _L("Remaning errors") + ":\n";
+ tooltip += _L("Remaining errors") + ":\n";
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n";
}
@@ -1397,19 +1398,31 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false
if (m_objects_model->GetItemType(item)&itInstance)
item = m_objects_model->GetItemById(obj_idx);
+ wxArrayString input_files;
+ if (from_galery) {
+ GalleryDialog dlg(this);
+ if (dlg.ShowModal() != wxID_CLOSE)
+ dlg.get_input_files(input_files);
+ }
+ else
+ wxGetApp().import_model(wxGetApp().tab_panel()->GetPage(0), input_files);
+
+ if (input_files.IsEmpty())
+ return;
+
+ take_snapshot((type == ModelVolumeType::MODEL_PART) ? _L("Load Part") : _L("Load Modifier"));
+
std::vector<ModelVolume*> volumes;
// ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common
/*
if (type == ModelVolumeType::MODEL_PART)
load_part(*(*m_objects)[obj_idx], volumes, type, from_galery);
else*/
- load_modifier(*(*m_objects)[obj_idx], volumes, type, from_galery);
+ load_modifier(input_files, *(*m_objects)[obj_idx], volumes, type, from_galery);
if (volumes.empty())
return;
- take_snapshot((type == ModelVolumeType::MODEL_PART) ? _L("Load Part") : _L("Load Modifier"));
-
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) {
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); });
@@ -1485,7 +1498,7 @@ void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>&
}
}
*/
-void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery)
+void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery)
{
// ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common
//if (type == ModelVolumeType::MODEL_PART)
@@ -1493,19 +1506,6 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
- wxArrayString input_files;
-
- if (from_galery) {
- GalleryDialog dlg(this);
- if (dlg.ShowModal() == wxID_CLOSE)
- return;
- dlg.get_input_files(input_files);
- if (input_files.IsEmpty())
- return;
- }
- else
- wxGetApp().import_model(parent, input_files);
-
wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().mainframe, wxPD_AUTO_HIDE);
wxBusyCursor busy;
@@ -1556,7 +1556,7 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
for (auto object : model.objects) {
if (model_object.origin_translation != Vec3d::Zero()) {
object->center_around_origin();
- Vec3d delta = model_object.origin_translation - object->origin_translation;
+ const Vec3d delta = model_object.origin_translation - object->origin_translation;
for (auto volume : object->volumes) {
volume->translate(delta);
}
@@ -1570,6 +1570,12 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum
new_volume->name = boost::filesystem::path(input_file).filename().string();
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
+ // update source data
+ new_volume->source.input_file = input_file;
+ new_volume->source.object_idx = obj_idx;
+ new_volume->source.volume_idx = int(model_object.volumes.size()) - 1;
+ if (model.objects.size() == 1 && model.objects.front()->volumes.size() == 1)
+ new_volume->source.mesh_offset = model.objects.front()->volumes.front()->source.mesh_offset;
if (from_galery) {
// Transform the new modifier to be aligned with the print bed.
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index 535bfa7a7..51d69eaee 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -251,7 +251,7 @@ public:
void load_subobject(ModelVolumeType type, bool from_galery = false);
// ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common
//void load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
- void load_modifier(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
+ void load_modifier(const wxArrayString& input_files, ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false);
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void load_shape_object(const std::string &type_name);
void load_shape_object_from_gallery();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index 393be1a4e..97ab7094c 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -50,13 +50,12 @@ float GLGizmoBase::Grabber::get_dragging_half_size(float size) const
void GLGizmoBase::Grabber::render(float size, const std::array<float, 4>& render_color, bool picking) const
{
- if (! cube_initialized) {
+ if (!cube.is_initialized()) {
// This cannot be done in constructor, OpenGL is not yet
// initialized at that point (on Linux at least).
indexed_triangle_set mesh = its_make_cube(1., 1., 1.);
its_translate(mesh, Vec3f(-0.5, -0.5, -0.5));
const_cast<GLModel&>(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } });
- const_cast<bool&>(cube_initialized) = true;
}
float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size));
@@ -90,15 +89,11 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
m_highlight_color = DEFAULT_HIGHLIGHT_COLOR;
- m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24));
- m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.));
- m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.));
}
void GLGizmoBase::set_hover_id(int id)
{
- if (m_grabbers.empty() || (id < (int)m_grabbers.size()))
- {
+ if (m_grabbers.empty() || id < (int)m_grabbers.size()) {
m_hover_id = id;
on_set_hover_id();
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index 66ca4fca2..1e2cd93fb 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -66,7 +66,6 @@ protected:
void render(float size, const std::array<float, 4>& render_color, bool picking) const;
GLModel cube;
- bool cube_initialized = false;
};
public:
@@ -105,9 +104,6 @@ protected:
bool m_first_input_window_render;
mutable std::string m_tooltip;
CommonGizmosDataPool* m_c;
- GLModel m_cone;
- GLModel m_cylinder;
- GLModel m_sphere;
public:
GLGizmoBase(GLCanvas3D& parent,
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index 691a86706..a001d5a81 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -20,7 +20,6 @@ namespace GUI {
GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
- m_vbo_cylinder.init_from(its_make_cylinder(1., 1.));
}
@@ -63,6 +62,9 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
void GLGizmoHollow::on_render()
{
+ if (!m_cylinder.is_initialized())
+ m_cylinder.init_from(its_make_cylinder(1.0, 1.0));
+
const Selection& selection = m_parent.get_selection();
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
@@ -148,11 +150,11 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
}
}
- const_cast<GLModel*>(&m_vbo_cylinder)->set_color(-1, render_color);
+ const_cast<GLModel*>(&m_cylinder)->set_color(-1, render_color);
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
- glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2)));
+ glsafe(::glTranslatef(drain_hole.pos.x(), drain_hole.pos.y(), drain_hole.pos.z()));
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
if (vol->is_left_handed())
@@ -166,7 +168,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons
glsafe(::glPushMatrix());
glsafe(::glTranslated(0., 0., -drain_hole.height));
glsafe(::glScaled(drain_hole.radius, drain_hole.radius, drain_hole.height + sla::HoleStickOutLength));
- m_vbo_cylinder.render();
+ m_cylinder.render();
glsafe(::glPopMatrix());
if (vol->is_left_handed())
@@ -549,9 +551,13 @@ RENDER_AGAIN:
m_imgui->text(m_desc.at("offset"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
ImGui::PushItemWidth(window_width - settings_sliders_left);
+#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
+ m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm", 1.0f, true, _L(opts[0].second->tooltip));
+#else
m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width);
+#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
bool slider_clicked = m_imgui->get_last_slider_status().clicked; // someone clicked the slider
bool slider_edited =m_imgui->get_last_slider_status().edited; // someone is dragging the slider
@@ -561,9 +567,13 @@ RENDER_AGAIN:
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("quality"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
+#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
+ m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f", 1.0f, true, _L(opts[1].second->tooltip));
+#else
m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width);
+#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
slider_clicked |= m_imgui->get_last_slider_status().clicked;
slider_edited |= m_imgui->get_last_slider_status().edited;
@@ -574,9 +584,13 @@ RENDER_AGAIN:
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("closing_distance"));
ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x);
+#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
+ m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm", 1.0f, true, _L(opts[2].second->tooltip));
+#else
m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
if (m_imgui->get_last_slider_status().hovered)
m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width);
+#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
slider_clicked |= m_imgui->get_last_slider_status().clicked;
slider_edited |= m_imgui->get_last_slider_status().edited;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
index 2cf08de2a..bc144c297 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp
@@ -48,7 +48,8 @@ private:
ObjectID m_old_mo_id = -1;
- GLModel m_vbo_cylinder;
+ GLModel m_cylinder;
+
float m_new_hole_radius = 2.f; // Size of a new hole.
float m_new_hole_height = 6.f;
mutable std::vector<bool> m_selected; // which holes are currently selected
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index e73a85647..d16f0ff2d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -20,7 +20,6 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam
, m_starting_box_center(Vec3d::Zero())
, m_starting_box_bottom_center(Vec3d::Zero())
{
- m_vbo_cone.init_from(its_make_cone(1., 1., 2*PI/36));
}
std::string GLGizmoMove3D::get_tooltip() const
@@ -89,6 +88,9 @@ void GLGizmoMove3D::on_update(const UpdateData& data)
void GLGizmoMove3D::on_render()
{
+ if (!m_cone.is_initialized())
+ m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 18.0));
+
const Selection& selection = m_parent.get_selection();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
@@ -205,7 +207,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
if (shader == nullptr)
return;
- const_cast<GLModel*>(&m_vbo_cone)->set_color(-1, color);
+ const_cast<GLModel*>(&m_cone)->set_color(-1, color);
if (!picking) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
@@ -220,7 +222,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
glsafe(::glTranslated(0.0, 0.0, 2.0 * size));
glsafe(::glScaled(0.75 * size, 0.75 * size, 3.0 * size));
- m_vbo_cone.render();
+ m_cone.render();
glsafe(::glPopMatrix());
if (! picking)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
index baa2df739..2d331bfb5 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp
@@ -19,7 +19,7 @@ class GLGizmoMove3D : public GLGizmoBase
Vec3d m_starting_box_center;
Vec3d m_starting_box_bottom_center;
- GLModel m_vbo_cone;
+ GLModel m_cone;
public:
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 8b866c7c9..fa59d7646 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -18,13 +18,17 @@
namespace Slic3r::GUI {
+ std::shared_ptr<GLIndexedVertexArray> GLGizmoPainterBase::s_sphere = nullptr;
GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
- // Make sphere and save it into a vertex buffer.
- m_vbo_sphere.load_its_flat_shading(its_make_sphere(1., (2*M_PI)/24.));
- m_vbo_sphere.finalize_geometry(true);
+}
+
+GLGizmoPainterBase::~GLGizmoPainterBase()
+{
+ if (s_sphere != nullptr && s_sphere->has_VBOs())
+ s_sphere->release_geometry();
}
void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection)
@@ -184,6 +188,12 @@ void GLGizmoPainterBase::render_cursor_circle() const
void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
{
+ if (s_sphere == nullptr) {
+ s_sphere = std::make_shared<GLIndexedVertexArray>();
+ s_sphere->load_its_flat_shading(its_make_sphere(1.0, double(PI) / 12.0));
+ s_sphere->finalize_geometry(true);
+ }
+
const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix(true, true, false, true).inverse();
const bool is_left_handed = Geometry::Transformation(trafo).is_left_handed();
@@ -204,7 +214,8 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
render_color = this->get_cursor_sphere_right_button_color();
glsafe(::glColor4fv(render_color.data()));
- m_vbo_sphere.render();
+ assert(s_sphere != nullptr);
+ s_sphere->render();
if (is_left_handed)
glFrontFace(GL_CCW);
@@ -245,7 +256,7 @@ std::vector<std::vector<GLGizmoPainterBase::ProjectedMousePosition>> GLGizmoPain
const Camera &camera = wxGetApp().plater()->get_camera();
std::vector<ProjectedMousePosition> mesh_hit_points;
- mesh_hit_points.reserve(mouse_position.size());
+ mesh_hit_points.reserve(mouse_positions.size());
// In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
for (const Vec2d &mp : mouse_positions) {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
index 97ac8e4e9..ea6760a17 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp
@@ -12,6 +12,7 @@
#include <cereal/types/vector.hpp>
#include <GL/glew.h>
+#include <memory>
namespace Slic3r::GUI {
@@ -112,7 +113,7 @@ private:
void on_render_for_picking() override {}
public:
GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
- ~GLGizmoPainterBase() override = default;
+ virtual ~GLGizmoPainterBase() override;
virtual void set_painter_gizmo_data(const Selection& selection);
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
@@ -202,7 +203,7 @@ private:
const Camera& camera,
const std::vector<Transform3d>& trafo_matrices) const;
- GLIndexedVertexArray m_vbo_sphere;
+ static std::shared_ptr<GLIndexedVertexArray> s_sphere;
bool m_internal_stack_active = false;
bool m_schedule_update = false;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
index 61fe6e709..a234a19ff 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
@@ -39,20 +39,6 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
{
}
-GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
- : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
- , m_axis(other.m_axis)
- , m_angle(other.m_angle)
- , m_center(other.m_center)
- , m_radius(other.m_radius)
- , m_snap_coarse_in_radius(other.m_snap_coarse_in_radius)
- , m_snap_coarse_out_radius(other.m_snap_coarse_out_radius)
- , m_snap_fine_in_radius(other.m_snap_fine_in_radius)
- , m_snap_fine_out_radius(other.m_snap_fine_out_radius)
-{
-}
-
-
void GLGizmoRotate::set_angle(double angle)
{
if (std::abs(angle - 2.0 * (double)PI) < EPSILON)
@@ -130,6 +116,9 @@ void GLGizmoRotate::on_render()
if (!m_grabbers[0].enabled)
return;
+ if (!m_cone.is_initialized())
+ m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0));
+
const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
@@ -433,11 +422,8 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
+ , m_gizmos({ GLGizmoRotate(parent, GLGizmoRotate::X), GLGizmoRotate(parent, GLGizmoRotate::Y), GLGizmoRotate(parent, GLGizmoRotate::Z) })
{
- m_gizmos.emplace_back(parent, GLGizmoRotate::X);
- m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
- m_gizmos.emplace_back(parent, GLGizmoRotate::Z);
-
for (unsigned int i = 0; i < 3; ++i) {
m_gizmos[i].set_group_id(i);
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
index 3245c4dbe..af1ecf548 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp
@@ -4,7 +4,6 @@
#include "GLGizmoBase.hpp"
#include "../Jobs/RotoptimizeJob.hpp"
-
namespace Slic3r {
namespace GUI {
@@ -40,9 +39,10 @@ private:
mutable float m_snap_fine_in_radius;
mutable float m_snap_fine_out_radius;
+ GLModel m_cone;
+
public:
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
- GLGizmoRotate(const GLGizmoRotate& other);
virtual ~GLGizmoRotate() = default;
double get_angle() const { return m_angle; }
@@ -74,7 +74,7 @@ private:
class GLGizmoRotate3D : public GLGizmoBase
{
- std::vector<GLGizmoRotate> m_gizmos;
+ std::array<GLGizmoRotate, 3> m_gizmos;
public:
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 29e1fd2f3..51551281a 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -77,6 +77,13 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
void GLGizmoSlaSupports::on_render()
{
+ if (!m_cone.is_initialized())
+ m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0));
+ if (!m_sphere.is_initialized())
+ m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0));
+ if (!m_cylinder.is_initialized())
+ m_cylinder.init_from(its_make_cylinder(1.0, 1.0, double(PI) / 12.0));
+
ModelObject* mo = m_c->selection_info()->model_object();
const Selection& selection = m_parent.get_selection();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index 35e6a7308..92d085f37 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -91,6 +91,10 @@ private:
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
ObjectID m_old_mo_id;
+ GLModel m_cone;
+ GLModel m_cylinder;
+ GLModel m_sphere;
+
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp
index fc61ab62d..55bcc90a9 100644
--- a/src/slic3r/GUI/HintNotification.cpp
+++ b/src/slic3r/GUI/HintNotification.cpp
@@ -417,9 +417,9 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
m_loaded_hints.emplace_back(hint_data);
// open preferences
} else if(dict["hypertext_type"] == "preferences") {
- int page = static_cast<Preset::Type>(std::atoi(dict["hypertext_preferences_page"].c_str()));
+ std::string page = dict["hypertext_preferences_page"];
std::string item = dict["hypertext_preferences_item"];
- HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page, item]() { wxGetApp().open_preferences(page, item); } };
+ HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page, item]() { wxGetApp().open_preferences(item, page); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "plater") {
std::string item = dict["hypertext_plater_item"];
@@ -924,7 +924,7 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp
}
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
- wxGetApp().open_preferences(2, "show_hints");
+ wxGetApp().open_preferences("show_hints", "GUI");
}
ImGui::PopStyleColor(5);
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 797ba24d6..273417eee 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -47,6 +47,7 @@
#include "GUI_ObjectList.hpp"
#include "GalleryDialog.hpp"
#include "NotificationManager.hpp"
+#include "Preferences.hpp"
#ifdef _WIN32
#include <dbt.h>
@@ -272,6 +273,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
if (m_plater != nullptr) {
m_plater->get_collapse_toolbar().set_enabled(wxGetApp().app_config->get("show_collapse_button") == "1");
m_plater->show_action_buttons(true);
+
+ preferences_dialog = new PreferencesDialog(this);
}
}
@@ -1094,7 +1097,7 @@ static wxMenu* generate_help_menu()
else
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), GCODEVIEWER_APP_NAME), _L("Show about dialog"),
[](wxCommandEvent&) { Slic3r::GUI::about(); });
- append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the day"), _L("Opens Tip of the day notification in bottom right corner or shows another tip if already opened."),
+ append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the Day"), _L("Opens Tip of the day notification in bottom right corner or shows another tip if already opened."),
[](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(false); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"),
@@ -1188,9 +1191,9 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { save_project(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#ifdef __APPLE__
- append_menu_item(fileMenu, wxID_ANY, _L("Save project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
+ append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
#else
- append_menu_item(fileMenu, wxID_ANY, _L("Save project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
+ append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
#endif // __APPLE__
[this](wxCommandEvent&) { save_project_as(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save_as(); }, this);
@@ -1202,11 +1205,11 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
- append_menu_item(import_menu, wxID_ANY, _L("Import STL (imperial units)"), _L("Load an model saved with imperial units"),
+ append_menu_item(import_menu, wxID_ANY, _L("Import STL (Imperial Units)"), _L("Load an model saved with imperial units"),
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(true); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
- append_menu_item(import_menu, wxID_ANY, _L("Import SL1 / SL1S archive") + dots, _L("Load an SL1 / Sl1S archive"),
+ append_menu_item(import_menu, wxID_ANY, _L("Import SL1 / SL1S Archive") + dots, _L("Load an SL1 / Sl1S archive"),
[this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr && !m_plater->is_any_job_running(); }, this);
@@ -1214,7 +1217,7 @@ void MainFrame::init_menubar_as_editor()
append_menu_item(import_menu, wxID_ANY, _L("Import &Config") + dots + "\tCtrl+L", _L("Load exported configuration file"),
[this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr,
[]() {return true; }, this);
- append_menu_item(import_menu, wxID_ANY, _L("Import Config from &project") + dots +"\tCtrl+Alt+L", _L("Load configuration from project file"),
+ append_menu_item(import_menu, wxID_ANY, _L("Import Config from &Project") + dots +"\tCtrl+Alt+L", _L("Load configuration from project file"),
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config", nullptr,
[]() {return true; }, this);
import_menu->AppendSeparator();
@@ -1232,22 +1235,22 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
[this](){return can_send_gcode(); }, this);
m_changeable_menu_items.push_back(item_send_gcode);
- append_menu_item(export_menu, wxID_ANY, _L("Export G-code to SD card / Flash drive") + dots + "\tCtrl+U", _L("Export current plate as G-code to SD card / Flash drive"),
+ append_menu_item(export_menu, wxID_ANY, _L("Export G-code to SD Card / Flash Drive") + dots + "\tCtrl+U", _L("Export current plate as G-code to SD card / Flash drive"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(true); }, "export_to_sd", nullptr,
[this]() {return can_export_gcode_sd(); }, this);
export_menu->AppendSeparator();
- append_menu_item(export_menu, wxID_ANY, _L("Export plate as &STL") + dots, _L("Export current plate as STL"),
+ append_menu_item(export_menu, wxID_ANY, _L("Export Plate as &STL") + dots, _L("Export current plate as STL"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
[this](){return can_export_model(); }, this);
- append_menu_item(export_menu, wxID_ANY, _L("Export plate as STL &including supports") + dots, _L("Export current plate as STL including supports"),
+ append_menu_item(export_menu, wxID_ANY, _L("Export Plate as STL &Including Supports") + dots, _L("Export current plate as STL including supports"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr,
[this](){return can_export_supports(); }, this);
// Deprecating AMF export. Let's wait for user feedback.
-// append_menu_item(export_menu, wxID_ANY, _L("Export plate as &AMF") + dots, _L("Export current plate as AMF"),
+// append_menu_item(export_menu, wxID_ANY, _L("Export Plate as &AMF") + dots, _L("Export current plate as AMF"),
// [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr,
// [this](){return can_export_model(); }, this);
export_menu->AppendSeparator();
- append_menu_item(export_menu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
+ append_menu_item(export_menu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
export_menu->AppendSeparator();
@@ -1262,7 +1265,7 @@ void MainFrame::init_menubar_as_editor()
[]() {return true; }, this);
append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), "");
- append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD card / Flash drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."),
+ append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."),
[this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr,
[this]() {return can_eject(); }, this);
@@ -1298,7 +1301,7 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr,
[]() { return true; }, this);
fileMenu->AppendSeparator();
- append_menu_item(fileMenu, wxID_ANY, _L("&G-code preview") + dots, _L("Open G-code viewer"),
+ append_menu_item(fileMenu, wxID_ANY, _L("&G-code Preview") + dots, _L("Open G-code viewer"),
[this](wxCommandEvent&) { start_new_gcodeviewer_open_file(this); }, "", nullptr);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME),
@@ -1316,17 +1319,17 @@ void MainFrame::init_menubar_as_editor()
#else
wxString hotkey_delete = "Del";
#endif
- append_menu_item(editMenu, wxID_ANY, _L("&Select all") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A",
+ append_menu_item(editMenu, wxID_ANY, _L("&Select All") + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A",
_L("Selects all objects"), [this](wxCommandEvent&) { m_plater->select_all(); },
"", nullptr, [this](){return can_select(); }, this);
- append_menu_item(editMenu, wxID_ANY, _L("D&eselect all") + sep + "Esc",
+ append_menu_item(editMenu, wxID_ANY, _L("D&eselect All") + sep + "Esc",
_L("Deselects all objects"), [this](wxCommandEvent&) { m_plater->deselect_all(); },
"", nullptr, [this](){return can_deselect(); }, this);
editMenu->AppendSeparator();
- append_menu_item(editMenu, wxID_ANY, _L("&Delete selected") + sep + hotkey_delete,
+ append_menu_item(editMenu, wxID_ANY, _L("&Delete Selected") + sep + hotkey_delete,
_L("Deletes the current selection"),[this](wxCommandEvent&) { m_plater->remove_selected(); },
"remove_menu", nullptr, [this](){return can_delete(); }, this);
- append_menu_item(editMenu, wxID_ANY, _L("Delete &all") + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete,
+ append_menu_item(editMenu, wxID_ANY, _L("Delete &All") + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete,
_L("Deletes all objects"), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
"delete_all_menu", nullptr, [this](){return can_delete_all(); }, this);
@@ -1348,11 +1351,11 @@ void MainFrame::init_menubar_as_editor()
editMenu->AppendSeparator();
#ifdef __APPLE__
- append_menu_item(editMenu, wxID_ANY, _L("Re&load from disk") + dots + "\tCtrl+Shift+R",
+ append_menu_item(editMenu, wxID_ANY, _L("Re&load from Disk") + dots + "\tCtrl+Shift+R",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
"", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
#else
- append_menu_item(editMenu, wxID_ANY, _L("Re&load from disk") + sep + "F5",
+ append_menu_item(editMenu, wxID_ANY, _L("Re&load from Disk") + sep + "F5",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
"", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
#endif // __APPLE__
@@ -1410,11 +1413,11 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, []() {return true; }, this);
windowMenu->AppendSeparator();
- append_menu_item(windowMenu, wxID_ANY, _L("Open new instance") + "\tCtrl+Shift+I", _L("Open a new PrusaSlicer instance"),
+ append_menu_item(windowMenu, wxID_ANY, _L("Open New Instance") + "\tCtrl+Shift+I", _L("Open a new PrusaSlicer instance"),
[](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, [this]() {return m_plater != nullptr && wxGetApp().app_config->get("single_instance") != "1"; }, this);
windowMenu->AppendSeparator();
- append_menu_item(windowMenu, wxID_ANY, _L("Compare presets")/* + "\tCtrl+F"*/, _L("Compare presets"),
+ append_menu_item(windowMenu, wxID_ANY, _L("Compare Presets")/* + "\tCtrl+F"*/, _L("Compare presets"),
[this](wxCommandEvent&) { diff_dialog.show();}, "compare", nullptr, []() {return true; }, this);
}
@@ -1424,7 +1427,7 @@ void MainFrame::init_menubar_as_editor()
viewMenu = new wxMenu();
add_common_view_menu_items(viewMenu, this, std::bind(&MainFrame::can_change_view, this));
viewMenu->AppendSeparator();
- append_menu_check_item(viewMenu, wxID_ANY, _L("Show &labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
+ append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + sep + "E", _L("Show object/instance labels in 3D scene"),
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
#if ENABLE_PREVIEW_LAYOUT
@@ -1432,12 +1435,12 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { m_plater->show_legend(!m_plater->is_legend_shown()); }, this,
[this]() { return m_plater->is_preview_shown(); }, [this]() { return m_plater->is_legend_shown(); }, this);
#endif // ENABLE_PREVIEW_LAYOUT
- append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"),
+ append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse Sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"),
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
[]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
#ifndef __APPLE__
// OSX adds its own menu item to toggle fullscreen.
- append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"),
+ append_menu_check_item(viewMenu, wxID_ANY, _L("&Fullscreen") + "\t" + "F11", _L("Fullscreen"),
[this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen(),
// wxFULLSCREEN_ALL: wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION
wxFULLSCREEN_NOSTATUSBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION); },
@@ -1524,16 +1527,16 @@ void MainFrame::init_menubar_as_gcodeviewer()
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->load_gcode(); }, "open", nullptr,
[this]() {return m_plater != nullptr; }, this);
#ifdef __APPLE__
- append_menu_item(fileMenu, wxID_ANY, _L("Re&load from disk") + dots + "\tCtrl+Shift+R",
+ append_menu_item(fileMenu, wxID_ANY, _L("Re&load from Disk") + dots + "\tCtrl+Shift+R",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); },
"", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this);
#else
- append_menu_item(fileMenu, wxID_ANY, _L("Re&load from disk") + sep + "F5",
+ append_menu_item(fileMenu, wxID_ANY, _L("Re&load from Disk") + sep + "F5",
_L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); },
"", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this);
#endif // __APPLE__
fileMenu->AppendSeparator();
- append_menu_item(fileMenu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
+ append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
append_menu_item(fileMenu, wxID_ANY, _L("Open &PrusaSlicer") + dots, _L("Open PrusaSlicer"),
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 951ed70a1..f385ee8f8 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -32,6 +32,7 @@ class Tab;
class PrintHostQueueDialog;
class Plater;
class MainFrame;
+class PreferencesDialog;
enum QuickSlice
{
@@ -203,6 +204,7 @@ public:
DiffPresetDialog diff_dialog;
wxWindow* m_plater_page{ nullptr };
// wxProgressDialog* m_progress_dialog { nullptr };
+ PreferencesDialog* preferences_dialog { nullptr };
PrintHostQueueDialog* m_printhost_queue_dlg;
// std::shared_ptr<ProgressStatusBar> m_statusbar;
diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp
index 56e990577..80a8159c0 100644
--- a/src/slic3r/GUI/MsgDialog.cpp
+++ b/src/slic3r/GUI/MsgDialog.cpp
@@ -63,11 +63,25 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
SetSizerAndFit(main_sizer);
}
+void MsgDialog::SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus/* = false*/)
+{
+ if (wxButton* btn = get_button(btn_id)) {
+ btn->SetLabel(label);
+ if (set_focus)
+ btn->SetFocus();
+ }
+}
+
wxButton* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, const wxString& label/* = wxString()*/)
{
wxButton* btn = new wxButton(this, btn_id, label);
- if (set_focus)
+ if (set_focus) {
btn->SetFocus();
+ // For non-MSW platforms SetFocus is not enought to use it as default, when the dialog is closed by ENTER
+ // We have to set this button as the (permanently) default one in its dialog
+ // See https://twitter.com/ZMelmed/status/1472678454168539146
+ btn->SetDefault();
+ }
btn_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, HORIZ_SPACING);
btn->Bind(wxEVT_BUTTON, [this, btn_id](wxCommandEvent&) { this->EndModal(btn_id); });
return btn;
@@ -98,7 +112,7 @@ void MsgDialog::finalize()
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
-static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxString msg, bool monospaced_font = false)
+static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxString msg, bool monospaced_font = false, bool is_marked_msg = false)
{
wxHtmlWindow* html = new wxHtmlWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
@@ -122,7 +136,11 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
wxFont monospace = wxGetApp().code_font();
+#ifdef _WIN32
wxColour text_clr = wxGetApp().get_label_clr_default();
+#else
+ wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+#endif
wxColour bgr_clr = parent->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
@@ -134,10 +152,23 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
// calculate html page size from text
wxSize page_size;
int em = wxGetApp().em_unit();
+ if (!wxGetApp().mainframe) {
+ // If mainframe is nullptr, it means that GUI_App::on_init_inner() isn't completed
+ // (We just show information dialog about configuration version now)
+ // And as a result the em_unit value wasn't created yet
+ // So, calculate it from the scale factor of Dialog
+#if defined(__WXGTK__)
+ // Linux specific issue : get_dpi_for_window(this) still doesn't responce to the Display's scale in new wxWidgets(3.1.3).
+ // So, initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
+ em = std::max<size_t>(10, parent->GetTextExtent("m").x - 1);
+#else
+ double scale_factor = (double)get_dpi_for_window(parent) / (double)DPI_DEFAULT;
+ em = std::max<size_t>(10, 10.0f * scale_factor);
+#endif // __WXGTK__
+ }
// if message containes the table
- bool is_marked = msg.Contains("<tr>");
- if (is_marked) {
+ if (msg.Contains("<tr>")) {
int lines = msg.Freq('\n') + 1;
int pos = 0;
while (pos < (int)msg.Len() && pos != wxNOT_FOUND) {
@@ -155,7 +186,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin
}
html->SetMinSize(page_size);
- std::string msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked);
+ std::string msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked_msg);
boost::replace_all(msg_escaped, "\r\n", "<br>");
boost::replace_all(msg_escaped, "\n", "<br>");
if (monospaced_font)
@@ -243,11 +274,11 @@ int RichMessageDialog::ShowModal()
// InfoDialog
-InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg)
- : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title, wxOK | wxICON_INFORMATION)
+InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg, bool is_marked_msg/* = false*/, long style/* = wxOK | wxICON_INFORMATION*/)
+ : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title, style)
, msg(msg)
{
- add_msg_content(this, content_sizer, msg);
+ add_msg_content(this, content_sizer, msg, false, is_marked_msg);
finalize();
}
diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp
index 9fde187a6..17d935495 100644
--- a/src/slic3r/GUI/MsgDialog.hpp
+++ b/src/slic3r/GUI/MsgDialog.hpp
@@ -30,7 +30,7 @@ struct MsgDialog : wxDialog
MsgDialog &operator=(const MsgDialog &) = delete;
virtual ~MsgDialog() = default;
- // TODO: refactor with CreateStdDialogButtonSizer usage
+ void SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus = false);
protected:
enum {
@@ -111,6 +111,7 @@ public:
class MessageDialog : public MsgDialog
{
public:
+ // NOTE! Don't change a signature of contsrucor. It have to be tha same as for wxMessageDialog
MessageDialog( wxWindow *parent,
const wxString& message,
const wxString& caption = wxEmptyString,
@@ -130,6 +131,7 @@ class RichMessageDialog : public MsgDialog
bool m_checkBoxValue{ false };
public:
+ // NOTE! Don't change a signature of contsrucor. It have to be tha same as for wxRichMessageDialog
RichMessageDialog( wxWindow *parent,
const wxString& message,
const wxString& caption = wxEmptyString,
@@ -293,7 +295,9 @@ public:
const wxString& message,
const wxString& caption = wxEmptyString,
long style = wxOK)
- : wxRichMessageDialog(parent, message, caption, style) {}
+ : wxRichMessageDialog(parent, message, caption, style) {
+ this->SetEscapeId(wxID_CANCEL);
+ }
~RichMessageDialog() {}
};
#endif
@@ -302,7 +306,7 @@ public:
class InfoDialog : public MsgDialog
{
public:
- InfoDialog(wxWindow *parent, const wxString &title, const wxString &msg);
+ InfoDialog(wxWindow *parent, const wxString &title, const wxString &msg, bool is_marked = false, long style = wxOK| wxICON_INFORMATION);
InfoDialog(InfoDialog&&) = delete;
InfoDialog(const InfoDialog&) = delete;
InfoDialog&operator=(InfoDialog&&) = delete;
diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp
index 6039dcbcb..64fc4b8c8 100644
--- a/src/slic3r/GUI/OptionsGroup.cpp
+++ b/src/slic3r/GUI/OptionsGroup.cpp
@@ -984,6 +984,8 @@ bool OptionsGroup::launch_browser(const std::string& path_end)
RichMessageDialog dialog(parent, _L("Open hyperlink in default browser?"), _L("PrusaSlicer: Open hyperlink"), wxYES_NO);
dialog.ShowCheckBox(_L("Remember my choice"));
int answer = dialog.ShowModal();
+ if (answer == wxID_CANCEL)
+ return false;
if (dialog.IsCheckBoxChecked()) {
wxString preferences_item = _L("Suppress to open hyperlink in browser");
diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp
index 6647740dd..29d8baaa6 100644
--- a/src/slic3r/GUI/OptionsGroup.hpp
+++ b/src/slic3r/GUI/OptionsGroup.hpp
@@ -74,6 +74,12 @@ public:
label(_(label)), label_tooltip(_(tooltip)) {}
Line() : m_is_separator(true) {}
+ Line(const std::string& opt_key, const wxString& label, const wxString& tooltip) :
+ label(_(label)), label_tooltip(_(tooltip))
+ {
+ m_options.push_back(Option({ opt_key, coNone }, opt_key));
+ }
+
bool is_separator() const { return m_is_separator; }
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
@@ -180,6 +186,10 @@ public:
// if we have to set the same control alignment for different option groups,
// we have to set same max contrtol width to all of them
void set_max_win_width(int max_win_width);
+ void set_use_custom_ctrl(bool use_custom_ctrl) { m_use_custom_ctrl = use_custom_ctrl; }
+ const std::map<t_config_option_key, Option>& get_optioms_map() { return m_options; }
+
+ bool is_activated() { return sizer != nullptr; }
protected:
std::map<t_config_option_key, Option> m_options;
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index e8b9ae491..1b3c001a4 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -223,6 +223,9 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4);
sla_hidden_items = { label_volume, info_volume, /*label_materials, info_materials*/ };
+
+ // Fixes layout issues on plater, short BitmapComboBoxes with some Windows scaling, see GH issue #7414.
+ this->Show(false);
}
void ObjectInfo::show_sizer(bool show)
@@ -1182,10 +1185,10 @@ void Sidebar::jump_to_option(const std::string& opt_key, Preset::Type type, cons
void Sidebar::jump_to_option(size_t selected)
{
const Search::Option& opt = p->searcher.get_option(selected);
- wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key(), boost::nowide::narrow(opt.category));
-
- // Switch to the Settings NotePad
-// wxGetApp().mainframe->select_tab();
+ if (opt.type == Preset::TYPE_PREFERENCES)
+ wxGetApp().open_preferences(opt.opt_key(), boost::nowide::narrow(opt.group));
+ else
+ wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key(), boost::nowide::narrow(opt.category));
}
ObjectManipulation* Sidebar::obj_manipul()
@@ -3655,12 +3658,12 @@ void Plater::priv::reload_from_disk()
if (has_source || has_name) {
int new_volume_idx = -1;
int new_object_idx = -1;
- if (has_source) {
- // take idxs from source
- new_volume_idx = old_volume->source.volume_idx;
- new_object_idx = old_volume->source.object_idx;
- }
- else {
+// if (has_source) {
+// // take idxs from source
+// new_volume_idx = old_volume->source.volume_idx;
+// new_object_idx = old_volume->source.object_idx;
+// }
+// else {
// take idxs from the 1st matching volume
for (size_t o = 0; o < new_model.objects.size(); ++o) {
ModelObject* obj = new_model.objects[o];
@@ -3676,39 +3679,40 @@ void Plater::priv::reload_from_disk()
if (found)
break;
}
- }
+// }
- if (new_object_idx < 0 && (int)new_model.objects.size() <= new_object_idx) {
+ if (new_object_idx < 0 || int(new_model.objects.size()) <= new_object_idx) {
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
ModelObject* new_model_object = new_model.objects[new_object_idx];
- if (new_volume_idx < 0 && (int)new_model.objects.size() <= new_volume_idx) {
+ if (new_volume_idx < 0 || int(new_model_object->volumes.size()) <= new_volume_idx) {
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
- if (new_volume_idx < (int)new_model_object->volumes.size()) {
- old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
- ModelVolume* new_volume = old_model_object->volumes.back();
- new_volume->set_new_unique_id();
- new_volume->config.apply(old_volume->config);
- new_volume->set_type(old_volume->type());
- new_volume->set_material_id(old_volume->material_id());
- new_volume->set_transformation(old_volume->get_transformation());
- new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
- assert(! old_volume->source.is_converted_from_inches || ! old_volume->source.is_converted_from_meters);
- if (old_volume->source.is_converted_from_inches)
- new_volume->convert_from_imperial_units();
- else if (old_volume->source.is_converted_from_meters)
- new_volume->convert_from_meters();
- std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
- old_model_object->delete_volume(old_model_object->volumes.size() - 1);
- if (!sinking)
- old_model_object->ensure_on_bed();
- old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
-
- sla::reproject_points_and_holes(old_model_object);
- }
+
+ old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
+ ModelVolume* new_volume = old_model_object->volumes.back();
+ new_volume->set_new_unique_id();
+ new_volume->config.apply(old_volume->config);
+ new_volume->set_type(old_volume->type());
+ new_volume->set_material_id(old_volume->material_id());
+ new_volume->set_transformation(old_volume->get_transformation());
+ new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
+ new_volume->source.object_idx = old_volume->source.object_idx;
+ new_volume->source.volume_idx = old_volume->source.volume_idx;
+ assert(! old_volume->source.is_converted_from_inches || ! old_volume->source.is_converted_from_meters);
+ if (old_volume->source.is_converted_from_inches)
+ new_volume->convert_from_imperial_units();
+ else if (old_volume->source.is_converted_from_meters)
+ new_volume->convert_from_meters();
+ std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
+ old_model_object->delete_volume(old_model_object->volumes.size() - 1);
+ if (!sinking)
+ old_model_object->ensure_on_bed();
+ old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
+
+ sla::reproject_points_and_holes(old_model_object);
}
}
}
@@ -5717,23 +5721,26 @@ void Plater::export_stl(bool extended, bool selection_only)
return;
// Following lambda generates a combined mesh for export with normals pointing outwards.
- auto mesh_to_export = [](const ModelObject* mo, bool instances) -> TriangleMesh {
+ auto mesh_to_export = [](const ModelObject& mo, int instance_id) {
TriangleMesh mesh;
- for (const ModelVolume *v : mo->volumes)
+ for (const ModelVolume* v : mo.volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
- if (instances) {
+ if (instance_id == -1) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();
- for (const ModelInstance *i : mo->instances) {
+ for (const ModelInstance* i : mo.instances) {
TriangleMesh m = vols_mesh;
m.transform(i->get_matrix(), true);
mesh.merge(m);
}
}
+ else if (0 <= instance_id && instance_id < mo.instances.size())
+ mesh.transform(mo.instances[instance_id]->get_matrix(), true);
+
return mesh;
};
@@ -5742,14 +5749,8 @@ void Plater::export_stl(bool extended, bool selection_only)
if (selection_only) {
const ModelObject* model_object = p->model.objects[obj_idx];
if (selection.get_mode() == Selection::Instance)
- {
- if (selection.is_single_full_object())
- mesh = mesh_to_export(model_object, true);
- else
- mesh = mesh_to_export(model_object, false);
- }
- else
- {
+ mesh = selection.is_single_full_object() ? mesh_to_export(*model_object, -1) : mesh_to_export(*model_object, selection.get_instance_idx());
+ else {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
mesh = model_object->volumes[volume->volume_idx()]->mesh();
mesh.transform(volume->get_volume_transformation().get_matrix(), true);
@@ -5757,69 +5758,62 @@ void Plater::export_stl(bool extended, bool selection_only)
}
}
else {
- for (const ModelObject *o : p->model.objects)
- mesh.merge(mesh_to_export(o, true));
+ for (const ModelObject* o : p->model.objects) {
+ mesh.merge(mesh_to_export(*o, -1));
+ }
}
}
- else
- {
+ else {
// This is SLA mode, all objects have only one volume.
// However, we must have a look at the backend to load
// hollowed mesh and/or supports
const PrintObjects& objects = p->sla_print.objects();
- for (const SLAPrintObject* object : objects)
- {
+ for (const SLAPrintObject* object : objects) {
const ModelObject* model_object = object->model_object();
if (selection_only) {
if (model_object->id() != p->model.objects[obj_idx]->id())
continue;
}
- Transform3d mesh_trafo_inv = object->trafo().inverse();
- bool is_left_handed = object->is_left_handed();
+ const Transform3d mesh_trafo_inv = object->trafo().inverse();
+ const bool is_left_handed = object->is_left_handed();
TriangleMesh pad_mesh;
- bool has_pad_mesh = extended && object->has_mesh(slaposPad);
- if (has_pad_mesh)
- {
+ const bool has_pad_mesh = extended && object->has_mesh(slaposPad);
+ if (has_pad_mesh) {
pad_mesh = object->get_mesh(slaposPad);
pad_mesh.transform(mesh_trafo_inv);
}
TriangleMesh supports_mesh;
- bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree);
- if (has_supports_mesh)
- {
+ const bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree);
+ if (has_supports_mesh) {
supports_mesh = object->get_mesh(slaposSupportTree);
supports_mesh.transform(mesh_trafo_inv);
}
const std::vector<SLAPrintObject::Instance>& obj_instances = object->instances();
- for (const SLAPrintObject::Instance& obj_instance : obj_instances)
- {
+ for (const SLAPrintObject::Instance& obj_instance : obj_instances) {
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
assert(it != model_object->instances.end());
- if (it != model_object->instances.end())
- {
- bool one_inst_only = selection_only && ! selection.is_single_full_object();
+ if (it != model_object->instances.end()) {
+ const bool one_inst_only = selection_only && ! selection.is_single_full_object();
- int instance_idx = it - model_object->instances.begin();
+ const int instance_idx = it - model_object->instances.begin();
const Transform3d& inst_transform = one_inst_only
? Transform3d::Identity()
: object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
TriangleMesh inst_mesh;
- if (has_pad_mesh)
- {
+ if (has_pad_mesh) {
TriangleMesh inst_pad_mesh = pad_mesh;
inst_pad_mesh.transform(inst_transform, is_left_handed);
inst_mesh.merge(inst_pad_mesh);
}
- if (has_supports_mesh)
- {
+ if (has_supports_mesh) {
TriangleMesh inst_supports_mesh = supports_mesh;
inst_supports_mesh.transform(inst_transform, is_left_handed);
inst_mesh.merge(inst_supports_mesh);
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index 5bc7c981e..fa1e84ea6 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -42,22 +42,37 @@ namespace Slic3r {
namespace GUI {
-PreferencesDialog::PreferencesDialog(wxWindow* parent, int selected_tab, const std::string& highlight_opt_key) :
+PreferencesDialog::PreferencesDialog(wxWindow* parent) :
DPIDialog(parent, wxID_ANY, _L("Preferences"), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
#ifdef __WXOSX__
isOSX = true;
#endif
- build(selected_tab);
+ build();
+
+ m_highlighter.set_timer_owner(this, 0);
+}
+
+void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::string()*/, const std::string& tab_name/*= std::string()*/)
+{
+ int selected_tab = 0;
+ for (selected_tab; selected_tab < tabs->GetPageCount(); selected_tab++)
+ if (tabs->GetPageText(selected_tab) == _(tab_name))
+ break;
+ if (selected_tab < tabs->GetPageCount())
+ tabs->SetSelection(selected_tab);
+
if (!highlight_opt_key.empty())
init_highlighter(highlight_opt_key);
+
+ this->ShowModal();
}
static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& title, wxBookCtrlBase* tabs)
{
wxPanel* tab = new wxPanel(tabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
- tabs->AddPage(tab, title);
+ tabs->AddPage(tab, _(title));
tab->SetFont(wxGetApp().normal_font());
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
@@ -66,6 +81,7 @@ static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& tit
std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(tab);
optgroup->label_width = 40;
+ optgroup->set_config_category_and_type(title, int(Preset::TYPE_PREFERENCES));
return optgroup;
}
@@ -75,9 +91,67 @@ static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)
optgroup->update_visibility(comSimple);
wxBoxSizer* sizer = static_cast<wxBoxSizer*>(static_cast<wxPanel*>(optgroup->parent())->GetSizer());
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
+
+ // apply sercher
+ wxGetApp().sidebar().get_searcher().append_preferences_options(optgroup->get_lines());
}
-void PreferencesDialog::build(size_t selected_tab)
+static void append_bool_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
+ const std::string& opt_key,
+ const std::string& label,
+ const std::string& tooltip,
+ bool def_val,
+ ConfigOptionMode mode = comSimple)
+{
+ ConfigOptionDef def = {opt_key, coBool};
+ def.label = label;
+ def.tooltip = tooltip;
+ def.mode = mode;
+ def.set_default_value(new ConfigOptionBool{ def_val });
+ Option option(def, opt_key);
+ optgroup->append_single_option_line(option);
+
+ // fill data to the Search Dialog
+ wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
+}
+
+static void append_enum_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
+ const std::string& opt_key,
+ const std::string& label,
+ const std::string& tooltip,
+ const ConfigOption* def_val,
+ const t_config_enum_values *enum_keys_map,
+ std::initializer_list<std::string> enum_values,
+ std::initializer_list<std::string> enum_labels,
+ ConfigOptionMode mode = comSimple)
+{
+ ConfigOptionDef def = {opt_key, coEnum };
+ def.label = label;
+ def.tooltip = tooltip;
+ def.mode = mode;
+ def.enum_keys_map = enum_keys_map;
+ def.enum_values = std::vector<std::string>(enum_values);
+ def.enum_labels = std::vector<std::string>(enum_labels);
+
+ def.set_default_value(def_val);
+ Option option(def, opt_key);
+ optgroup->append_single_option_line(option);
+
+ // fill data to the Search Dialog
+ wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
+}
+
+static void append_preferences_option_to_searcer(std::shared_ptr<ConfigOptionsGroup> optgroup,
+ const std::string& opt_key,
+ const wxString& label)
+{
+ // fill data to the Search Dialog
+ wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
+ // apply sercher
+ wxGetApp().sidebar().get_searcher().append_preferences_option(Line(opt_key, label, ""));
+}
+
+void PreferencesDialog::build()
{
#ifdef _WIN32
wxGetApp().UpdateDarkUI(this);
@@ -90,20 +164,14 @@ void PreferencesDialog::build(size_t selected_tab)
auto app_config = get_app_config();
#ifdef _MSW_DARK_MODE
- wxBookCtrlBase* tabs;
-// if (wxGetApp().dark_mode())
- tabs = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME | wxNB_DEFAULT);
-/* else {
- tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME | wxNB_DEFAULT);
- tabs->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
- }*/
+ tabs = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME | wxNB_DEFAULT);
#else
- wxNotebook* tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL |wxNB_NOPAGETHEME | wxNB_DEFAULT );
+ tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL |wxNB_NOPAGETHEME | wxNB_DEFAULT );
tabs->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
// Add "General" tab
- m_optgroup_general = create_options_tab(_L("General"), tabs);
+ m_optgroup_general = create_options_tab(L("General"), tabs);
m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset" || opt_key == "default_action_on_new_project")
m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard";
@@ -113,215 +181,167 @@ void PreferencesDialog::build(size_t selected_tab)
bool is_editor = wxGetApp().is_editor();
- ConfigOptionDef def;
- Option option(def, "");
if (is_editor) {
- def.label = L("Remember output directory");
- def.type = coBool;
- def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory "
- "instead of the one containing the input files.");
- def.set_default_value(new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true });
- option = Option(def, "remember_output_path");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Auto-center parts");
- def.type = coBool;
- def.tooltip = L("If this is enabled, Slic3r will auto-center objects "
- "around the print bed center.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("autocenter") == "1" });
- option = Option(def, "autocenter");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Background processing");
- def.type = coBool;
- def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon "
- "as they\'re loaded in order to save time when exporting G-code.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("background_processing") == "1" });
- option = Option(def, "background_processing");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "remember_output_path",
+ L("Remember output directory"),
+ L("If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files."),
+ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true);
+
+ append_bool_option(m_optgroup_general, "autocenter",
+ L("Auto-center parts"),
+ L("If this is enabled, Slic3r will auto-center objects around the print bed center."),
+ app_config->get("autocenter") == "1");
+
+ append_bool_option(m_optgroup_general, "background_processing",
+ L("Background processing"),
+ L("If this is enabled, Slic3r will pre-process objects as soon "
+ "as they\'re loaded in order to save time when exporting G-code."),
+ app_config->get("background_processing") == "1");
m_optgroup_general->append_separator();
// Please keep in sync with ConfigWizard
- def.label = L("Export sources full pathnames to 3mf and amf");
- def.type = coBool;
- def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked.");
- def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1"));
- option = Option(def, "export_sources_full_pathnames");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "export_sources_full_pathnames",
+ L("Export sources full pathnames to 3mf and amf"),
+ L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked."),
+ app_config->get("export_sources_full_pathnames") == "1");
#ifdef _WIN32
// Please keep in sync with ConfigWizard
- def.label = L("Associate .3mf files to PrusaSlicer");
- def.type = coBool;
- def.tooltip = L("If enabled, sets PrusaSlicer as default application to open .3mf files.");
- def.set_default_value(new ConfigOptionBool(app_config->get("associate_3mf") == "1"));
- option = Option(def, "associate_3mf");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Associate .stl files to PrusaSlicer");
- def.type = coBool;
- def.tooltip = L("If enabled, sets PrusaSlicer as default application to open .stl files.");
- def.set_default_value(new ConfigOptionBool(app_config->get("associate_stl") == "1"));
- option = Option(def, "associate_stl");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "associate_3mf",
+ L("Associate .3mf files to PrusaSlicer"),
+ L("If enabled, sets PrusaSlicer as default application to open .3mf files."),
+ app_config->get("associate_3mf") == "1");
+
+ append_bool_option(m_optgroup_general, "associate_stl",
+ L("Associate .stl files to PrusaSlicer"),
+ L("If enabled, sets PrusaSlicer as default application to open .stl files."),
+ app_config->get("associate_stl") == "1");
#endif // _WIN32
m_optgroup_general->append_separator();
// Please keep in sync with ConfigWizard
- def.label = L("Update built-in Presets automatically");
- def.type = coBool;
- def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.");
- def.set_default_value(new ConfigOptionBool(app_config->get("preset_update") == "1"));
- option = Option(def, "preset_update");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Suppress \" - default - \" presets");
- def.type = coBool;
- def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer "
- "selections once there are any other valid presets available.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" });
- option = Option(def, "no_defaults");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Show incompatible print and filament presets");
- def.type = coBool;
- def.tooltip = L("When checked, the print and filament presets are shown in the preset editor "
- "even if they are marked as incompatible with the active printer");
- def.set_default_value(new ConfigOptionBool{ app_config->get("show_incompatible_presets") == "1" });
- option = Option(def, "show_incompatible_presets");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "preset_update",
+ L("Update built-in Presets automatically"),
+ L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded "
+ "into a separate temporary location. When a new preset version becomes available it is offered at application startup."),
+ app_config->get("preset_update") == "1");
+
+ append_bool_option(m_optgroup_general, "no_defaults",
+ L("Suppress \" - default - \" presets"),
+ L("Suppress \" - default - \" presets in the Print / Filament / Printer selections once there are any other valid presets available."),
+ app_config->get("no_defaults") == "1");
+
+ append_bool_option(m_optgroup_general, "show_incompatible_presets",
+ L("Show incompatible print and filament presets"),
+ L("When checked, the print and filament presets are shown in the preset editor "
+ "even if they are marked as incompatible with the active printer"),
+ app_config->get("show_incompatible_presets") == "1");
m_optgroup_general->append_separator();
- def.label = L("Show drop project dialog");
- def.type = coBool;
- def.tooltip = L("When checked, whenever dragging and dropping a project file on the application, shows a dialog asking to select the action to take on the file to load.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("show_drop_project_dialog") == "1" });
- option = Option(def, "show_drop_project_dialog");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "show_drop_project_dialog",
+ L("Show drop project dialog"),
+ L("When checked, whenever dragging and dropping a project file on the application, shows a dialog asking to select the action to take on the file to load."),
+ app_config->get("show_drop_project_dialog") == "1");
+ append_bool_option(m_optgroup_general, "single_instance",
#if __APPLE__
- def.label = L("Allow just a single PrusaSlicer instance");
- def.type = coBool;
- def.tooltip = L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances of same app from the command line. In such case this settings will allow only one instance.");
+ L("Allow just a single PrusaSlicer instance"),
+ L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances "
+ "of same app from the command line. In such case this settings will allow only one instance."),
#else
- def.label = L("Allow just a single PrusaSlicer instance");
- def.type = coBool;
- def.tooltip = L("If this is enabled, when starting PrusaSlicer and another instance of the same PrusaSlicer is already running, that instance will be reactivated instead.");
+ L("Allow just a single PrusaSlicer instance"),
+ L("If this is enabled, when starting PrusaSlicer and another instance of the same PrusaSlicer is already running, that instance will be reactivated instead."),
#endif
- def.set_default_value(new ConfigOptionBool{ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false });
- option = Option(def, "single_instance");
- m_optgroup_general->append_single_option_line(option);
+ app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false );
m_optgroup_general->append_separator();
- def.label = L("Ask to save unsaved changes when closing the application or when loading a new project");
- def.type = coBool;
- def.tooltip = L("Always ask for unsaved changes, when: \n"
+ append_bool_option(m_optgroup_general, "default_action_on_close_application",
+ L("Ask to save unsaved changes when closing the application or when loading a new project"),
+ L("Always ask for unsaved changes, when: \n"
"- Closing PrusaSlicer while some presets are modified,\n"
- "- Loading a new project while some presets are modified");
- def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_close_application") == "none" });
- option = Option(def, "default_action_on_close_application");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Ask for unsaved changes when selecting new preset");
- def.type = coBool;
- def.tooltip = L("Always ask for unsaved changes when selecting new preset or resetting a preset");
- def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_select_preset") == "none" });
- option = Option(def, "default_action_on_select_preset");
- m_optgroup_general->append_single_option_line(option);
-
- def.label = L("Ask for unsaved changes when creating new project");
- def.type = coBool;
- def.tooltip = L("Always ask for unsaved changes when creating new project");
- def.set_default_value(new ConfigOptionBool{ app_config->get("default_action_on_new_project") == "none" });
- option = Option(def, "default_action_on_new_project");
- m_optgroup_general->append_single_option_line(option);
+ "- Loading a new project while some presets are modified"),
+ app_config->get("default_action_on_close_application") == "none");
+
+ append_bool_option(m_optgroup_general, "default_action_on_select_preset",
+ L("Ask for unsaved changes when selecting new preset"),
+ L("Always ask for unsaved changes when selecting new preset or resetting a preset"),
+ app_config->get("default_action_on_select_preset") == "none");
+
+ append_bool_option(m_optgroup_general, "default_action_on_new_project",
+ L("Ask for unsaved changes when creating new project"),
+ L("Always ask for unsaved changes when creating new project"),
+ app_config->get("default_action_on_new_project") == "none");
}
#ifdef _WIN32
else {
- def.label = L("Associate .gcode files to PrusaSlicer G-code Viewer");
- def.type = coBool;
- def.tooltip = L("If enabled, sets PrusaSlicer G-code Viewer as default application to open .gcode files.");
- def.set_default_value(new ConfigOptionBool(app_config->get("associate_gcode") == "1"));
- option = Option(def, "associate_gcode");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "associate_gcode",
+ L("Associate .gcode files to PrusaSlicer G-code Viewer"),
+ L("If enabled, sets PrusaSlicer G-code Viewer as default application to open .gcode files."),
+ app_config->get("associate_gcode") == "1");
}
#endif // _WIN32
#if __APPLE__
- def.label = L("Use Retina resolution for the 3D scene");
- def.type = coBool;
- def.tooltip = L("If enabled, the 3D scene will be rendered in Retina resolution. "
- "If you are experiencing 3D performance problems, disabling this option may help.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("use_retina_opengl") == "1" });
- option = Option (def, "use_retina_opengl");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "use_retina_opengl",
+ L("Use Retina resolution for the 3D scene"),
+ L("If enabled, the 3D scene will be rendered in Retina resolution. "
+ "If you are experiencing 3D performance problems, disabling this option may help."),
+ app_config->get("use_retina_opengl") == "1");
#endif
m_optgroup_general->append_separator();
// Show/Hide splash screen
- def.label = L("Show splash screen");
- def.type = coBool;
- def.tooltip = L("Show splash screen");
- def.set_default_value(new ConfigOptionBool{ app_config->get("show_splash_screen") == "1" });
- option = Option(def, "show_splash_screen");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "show_splash_screen",
+ L("Show splash screen"),
+ L("Show splash screen"),
+ app_config->get("show_splash_screen") == "1");
// Clear Undo / Redo stack on new project
- def.label = L("Clear Undo / Redo stack on new project");
- def.type = coBool;
- def.tooltip = L("Clear Undo / Redo stack on new project or when an existing project is loaded.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("clear_undo_redo_stack_on_new_project") == "1" });
- option = Option(def, "clear_undo_redo_stack_on_new_project");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "clear_undo_redo_stack_on_new_project",
+ L("Clear Undo / Redo stack on new project"),
+ L("Clear Undo / Redo stack on new project or when an existing project is loaded."),
+ app_config->get("clear_undo_redo_stack_on_new_project") == "1");
#if defined(_WIN32) || defined(__APPLE__)
- def.label = L("Enable support for legacy 3DConnexion devices");
- def.type = coBool;
- def.tooltip = L("If enabled, the legacy 3DConnexion devices settings dialog is available by pressing CTRL+M");
- def.set_default_value(new ConfigOptionBool{ app_config->get("use_legacy_3DConnexion") == "1" });
- option = Option(def, "use_legacy_3DConnexion");
- m_optgroup_general->append_single_option_line(option);
+ append_bool_option(m_optgroup_general, "use_legacy_3DConnexion",
+ L("Enable support for legacy 3DConnexion devices"),
+ L("If enabled, the legacy 3DConnexion devices settings dialog is available by pressing CTRL+M"),
+ app_config->get("use_legacy_3DConnexion") == "1");
#endif // _WIN32 || __APPLE__
activate_options_tab(m_optgroup_general);
// Add "Camera" tab
- m_optgroup_camera = create_options_tab(_L("Camera"), tabs);
+ m_optgroup_camera = create_options_tab(L("Camera"), tabs);
m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
- def.label = L("Use perspective camera");
- def.type = coBool;
- def.tooltip = L("If enabled, use perspective camera. If not enabled, use orthographic camera.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("use_perspective_camera") == "1" });
- option = Option(def, "use_perspective_camera");
- m_optgroup_camera->append_single_option_line(option);
-
- def.label = L("Use free camera");
- def.type = coBool;
- def.tooltip = L("If enabled, use free camera. If not enabled, use constrained camera.");
- def.set_default_value(new ConfigOptionBool(app_config->get("use_free_camera") == "1"));
- option = Option(def, "use_free_camera");
- m_optgroup_camera->append_single_option_line(option);
-
- def.label = L("Reverse direction of zoom with mouse wheel");
- def.type = coBool;
- def.tooltip = L("If enabled, reverses the direction of zoom with mouse wheel");
- def.set_default_value(new ConfigOptionBool(app_config->get("reverse_mouse_wheel_zoom") == "1"));
- option = Option(def, "reverse_mouse_wheel_zoom");
- m_optgroup_camera->append_single_option_line(option);
+ append_bool_option(m_optgroup_camera, "use_perspective_camera",
+ L("Use perspective camera"),
+ L("If enabled, use perspective camera. If not enabled, use orthographic camera."),
+ app_config->get("use_perspective_camera") == "1");
+
+ append_bool_option(m_optgroup_camera, "use_free_camera",
+ L("Use free camera"),
+ L("If enabled, use free camera. If not enabled, use constrained camera."),
+ app_config->get("use_free_camera") == "1");
+
+ append_bool_option(m_optgroup_camera, "reverse_mouse_wheel_zoom",
+ L("Reverse direction of zoom with mouse wheel"),
+ L("If enabled, reverses the direction of zoom with mouse wheel"),
+ app_config->get("reverse_mouse_wheel_zoom") == "1");
activate_options_tab(m_optgroup_camera);
// Add "GUI" tab
- m_optgroup_gui = create_options_tab(_L("GUI"), tabs);
- m_optgroup_gui->m_on_change = [this, tabs](t_config_option_key opt_key, boost::any value) {
+ m_optgroup_gui = create_options_tab(L("GUI"), tabs);
+ m_optgroup_gui->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (opt_key == "suppress_hyperlinks")
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
else if (opt_key == "notify_release") {
@@ -343,165 +363,123 @@ void PreferencesDialog::build(size_t selected_tab)
}
};
- def.label = L("Sequential slider applied only to top layer");
- def.type = coBool;
- def.tooltip = L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer."
- "If disabled, changes made using the sequential slider, in preview, apply to the whole gcode.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("seq_top_layer_only") == "1" });
- option = Option(def, "seq_top_layer_only");
- m_optgroup_gui->append_single_option_line(option);
+ append_bool_option(m_optgroup_gui, "seq_top_layer_only",
+ L("Sequential slider applied only to top layer"),
+ L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer."
+ "If disabled, changes made using the sequential slider, in preview, apply to the whole gcode."),
+ app_config->get("seq_top_layer_only") == "1");
if (is_editor) {
- def.label = L("Show sidebar collapse/expand button");
- def.type = coBool;
- def.tooltip = L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene");
- def.set_default_value(new ConfigOptionBool{ app_config->get("show_collapse_button") == "1" });
- option = Option(def, "show_collapse_button");
- m_optgroup_gui->append_single_option_line(option);
-
- def.label = L("Suppress to open hyperlink in browser");
- def.type = coBool;
- def.tooltip = L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
- "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("suppress_hyperlinks") == "1" });
- option = Option(def, "suppress_hyperlinks");
- m_optgroup_gui->append_single_option_line(option);
-
- def.label = L("Use colors for axes values in Manipulation panel");
- def.type = coBool;
- def.tooltip = L("If enabled, the axes names and axes values will be colorized according to the axes colors. "
- "If disabled, old UI will be used.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("color_mapinulation_panel") == "1" });
- option = Option(def, "color_mapinulation_panel");
- m_optgroup_gui->append_single_option_line(option);
-
- def.label = L("Order object volumes by types");
- def.type = coBool;
- def.tooltip = L("If enabled, volumes will be always ordered inside the object. Correct order is Model Part, Negative Volume, Modifier, Support Blocker and Support Enforcer. "
- "If disabled, you can reorder Model Parts, Negative Volumes and Modifiers. But one of the model parts have to be on the first place.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("order_volumes") == "1" });
- option = Option(def, "order_volumes");
- m_optgroup_gui->append_single_option_line(option);
+ append_bool_option(m_optgroup_gui, "show_collapse_button",
+ L("Show sidebar collapse/expand button"),
+ L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene"),
+ app_config->get("show_collapse_button") == "1");
+
+ append_bool_option(m_optgroup_gui, "suppress_hyperlinks",
+ L("Suppress to open hyperlink in browser"),
+ L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
+ "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
+ app_config->get("suppress_hyperlinks") == "1");
+
+ append_bool_option(m_optgroup_gui, "color_mapinulation_panel",
+ L("Use colors for axes values in Manipulation panel"),
+ L("If enabled, the axes names and axes values will be colorized according to the axes colors. "
+ "If disabled, old UI will be used."),
+ app_config->get("color_mapinulation_panel") == "1");
+
+ append_bool_option(m_optgroup_gui, "order_volumes",
+ L("Order object volumes by types"),
+ L("If enabled, volumes will be always ordered inside the object. Correct order is Model Part, Negative Volume, Modifier, Support Blocker and Support Enforcer. "
+ "If disabled, you can reorder Model Parts, Negative Volumes and Modifiers. But one of the model parts have to be on the first place."),
+ app_config->get("order_volumes") == "1");
#ifdef _MSW_DARK_MODE
- def.label = L("Set settings tabs as menu items (experimental)");
- def.type = coBool;
- def.tooltip = L("If enabled, Settings Tabs will be placed as menu items. "
- "If disabled, old UI will be used.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("tabs_as_menu") == "1" });
- option = Option(def, "tabs_as_menu");
- m_optgroup_gui->append_single_option_line(option);
+ append_bool_option(m_optgroup_gui, "tabs_as_menu",
+ L("Set settings tabs as menu items (experimental)"),
+ L("If enabled, Settings Tabs will be placed as menu items. If disabled, old UI will be used."),
+ app_config->get("tabs_as_menu") == "1");
#endif
m_optgroup_gui->append_separator();
- def.label = L("Show \"Tip of the day\" notification after start");
- def.type = coBool;
- def.tooltip = L("If enabled, useful hints are displayed at startup.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("show_hints") == "1" });
- option = Option(def, "show_hints");
- m_optgroup_gui->append_single_option_line(option);
-
- ConfigOptionDef def_enum;
- def_enum.label = L("Notify about new releases");
- def_enum.type = coEnum;
- def_enum.tooltip = L("You will be notified about new release after startup acordingly: All = Regular release and alpha / beta releases. Release only = regular release.");
- def_enum.enum_keys_map = &ConfigOptionEnum<NotifyReleaseMode>::get_enum_values();
- def_enum.enum_values.push_back("all");
- def_enum.enum_values.push_back("release");
- def_enum.enum_values.push_back("none");
- def_enum.enum_labels.push_back(L("All"));
- def_enum.enum_labels.push_back(L("Release only"));
- def_enum.enum_labels.push_back(L("None"));
- def_enum.mode = comSimple;
- def_enum.set_default_value(new ConfigOptionEnum<NotifyReleaseMode>(static_cast<NotifyReleaseMode>(s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release")))));
- option = Option(def_enum, "notify_release");
- m_optgroup_gui->append_single_option_line(option);
+ append_bool_option(m_optgroup_gui, "show_hints",
+ L("Show \"Tip of the day\" notification after start"),
+ L("If enabled, useful hints are displayed at startup."),
+ app_config->get("show_hints") == "1");
- m_optgroup_gui->append_separator();
+ append_enum_option(m_optgroup_gui, "notify_release",
+ L("Notify about new releases"),
+ L("You will be notified about new release after startup acordingly: All = Regular release and alpha / beta releases. Release only = regular release."),
+ new ConfigOptionEnum<NotifyReleaseMode>(static_cast<NotifyReleaseMode>(s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release")))),
+ &ConfigOptionEnum<NotifyReleaseMode>::get_enum_values(),
+ {"all", "release", "none"},
+ {L("All"), L("Release only"), L("None")});
- def.label = L("Use custom size for toolbar icons");
- def.type = coBool;
- def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
- option = Option(def, "use_custom_toolbar_size");
- m_optgroup_gui->append_single_option_line(option);
+ m_optgroup_gui->append_separator();
+ append_bool_option(m_optgroup_gui, "use_custom_toolbar_size",
+ L("Use custom size for toolbar icons"),
+ L("If enabled, you can change size of toolbar icons manually."),
+ app_config->get("use_custom_toolbar_size") == "1");
}
activate_options_tab(m_optgroup_gui);
- // set Field for notify_release to its value to activate the object
+
if (is_editor) {
+ // set Field for notify_release to its value to activate the object
boost::any val = s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release"));
m_optgroup_gui->get_field("notify_release")->set_value(val, false);
- }
- if (is_editor) {
create_icon_size_slider();
m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
create_settings_mode_widget();
create_settings_text_color_widget();
- }
#if ENABLE_ENVIRONMENT_MAP
- if (is_editor) {
// Add "Render" tab
- m_optgroup_render = create_options_tab(_L("Render"), tabs);
+ m_optgroup_render = create_options_tab(L("Render"), tabs);
m_optgroup_render->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
- def.label = L("Use environment map");
- def.type = coBool;
- def.tooltip = L("If enabled, renders object using the environment map.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("use_environment_map") == "1" });
- option = Option(def, "use_environment_map");
- m_optgroup_render->append_single_option_line(option);
+ append_bool_option(m_optgroup_render, "use_environment_map",
+ L("Use environment map"),
+ L("If enabled, renders object using the environment map."),
+ app_config->get("use_environment_map") == "1");
activate_options_tab(m_optgroup_render);
- }
#endif // ENABLE_ENVIRONMENT_MAP
#ifdef _WIN32
- // Add "Dark Mode" tab
- {
// Add "Dark Mode" tab
m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs);
m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
- def.label = L("Enable dark mode");
- def.type = coBool;
- def.tooltip = L("If enabled, UI will use Dark mode colors. "
- "If disabled, old UI will be used.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("dark_color_mode") == "1" });
- option = Option(def, "dark_color_mode");
- m_optgroup_dark_mode->append_single_option_line(option);
+ append_bool_option(m_optgroup_dark_mode, "dark_color_mode",
+ L("Enable dark mode"),
+ L("If enabled, UI will use Dark mode colors. If disabled, old UI will be used."),
+ app_config->get("dark_color_mode") == "1");
if (wxPlatformInfo::Get().GetOSMajorVersion() >= 10) // Use system menu just for Window newer then Windows 10
// Use menu with ownerdrawn items by default on systems older then Windows 10
{
- def.label = L("Use system menu for application");
- def.type = coBool;
- def.tooltip = L("If enabled, application will use the standart Windows system menu,\n"
- "but on some combination of display scales it can looks ugly. If disabled, old UI will be used.");
- def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" });
- option = Option(def, "sys_menu_enabled");
- m_optgroup_dark_mode->append_single_option_line(option);
+ append_bool_option(m_optgroup_dark_mode, "sys_menu_enabled",
+ L("Use system menu for application"),
+ L("If enabled, application will use the standart Windows system menu,\n"
+ "but on some combination od display scales it can look ugly. If disabled, old UI will be used."),
+ app_config->get("sys_menu_enabled") == "1");
}
activate_options_tab(m_optgroup_dark_mode);
- }
#endif //_WIN32
+ }
// update alignment of the controls for all tabs
update_ctrls_alignment();
- if (selected_tab < tabs->GetPageCount())
- tabs->SetSelection(selected_tab);
-
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(tabs, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
@@ -526,7 +504,10 @@ std::vector<ConfigOptionsGroup*> PreferencesDialog::optgroups()
#ifdef _WIN32
, m_optgroup_dark_mode.get()
#endif // _WIN32
- })
+#if ENABLE_ENVIRONMENT_MAP
+ , m_optgroup_render.get()
+#endif // ENABLE_ENVIRONMENT_MAP
+ })
if (opt)
out.emplace_back(opt);
return out;
@@ -546,9 +527,6 @@ void PreferencesDialog::update_ctrls_alignment()
void PreferencesDialog::accept(wxEvent&)
{
-// if (m_values.find("no_defaults") != m_values.end()
-// warning_catcher(this, wxString::Format(_L("You need to restart %s to make the changes effective."), SLIC3R_APP_NAME));
-
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
for (const std::string& option : options_to_recreate_GUI) {
@@ -627,16 +605,29 @@ void PreferencesDialog::accept(wxEvent&)
wxGetApp().update_ui_from_settings();
}
-void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect)
+void PreferencesDialog::msw_rescale()
{
for (ConfigOptionsGroup* og : this->optgroups())
og->msw_rescale();
+#ifdef _WIN32
+ m_optgroup_dark_mode->msw_rescale();
+#endif //_WIN32
+#if ENABLE_ENVIRONMENT_MAP
+ m_optgroup_render->msw_rescale();
+#endif // ENABLE_ENVIRONMENT_MAP
msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL });
layout();
}
+void PreferencesDialog::on_sys_color_changed()
+{
+#ifdef _WIN32
+ wxGetApp().UpdateDlgDarkUI(this);
+#endif
+}
+
void PreferencesDialog::layout()
{
const int em = em_unit();
@@ -732,7 +723,8 @@ void PreferencesDialog::create_settings_mode_widget()
wxWindow* parent = m_optgroup_gui->parent();
wxGetApp().UpdateDarkUI(parent);
- wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _L("Layout Options"));
+ wxString title = L("Layout Options");
+ wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
wxGetApp().UpdateDarkUI(stb);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
stb->SetFont(wxGetApp().normal_font());
@@ -766,36 +758,58 @@ void PreferencesDialog::create_settings_mode_widget()
id++;
}
+ std::string opt_key = "settings_layout_mode";
+ m_blinkers[opt_key] = new BlinkingBitmap(this);
+
auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(m_blinkers[opt_key], 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
+
+ append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::create_settings_text_color_widget()
{
wxWindow* parent = m_optgroup_gui->parent();
- wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _L("Text colors"));
+ wxString title = L("Text colors");
+ wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
wxGetApp().UpdateDarkUI(stb);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
- wxSizer* sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
- ButtonsDescription::FillSizerWithTextColorDescriptions(sizer, parent, &m_sys_colour, &m_mod_colour);
+ std::string opt_key = "text_colors";
+ m_blinkers[opt_key] = new BlinkingBitmap(this);
+
+ wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
+ ButtonsDescription::FillSizerWithTextColorDescriptions(stb_sizer, parent, &m_sys_colour, &m_mod_colour);
+
+ auto sizer = new wxBoxSizer(wxHORIZONTAL);
+ sizer->Add(m_blinkers[opt_key], 0, wxALIGN_CENTER_VERTICAL);
+ sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
+
+ append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
{
- m_highlighter.set_timer_owner(this, 0);
- this->Bind(wxEVT_TIMER, [this](wxTimerEvent&)
- {
- m_highlighter.blink();
- });
+ if (m_blinkers.find(opt_key) != m_blinkers.end())
+ if (BlinkingBitmap* blinker = m_blinkers.at(opt_key); blinker) {
+ m_highlighter.init(blinker);
+ return;
+ }
- std::pair<OG_CustomCtrl*, bool*> ctrl = { nullptr, nullptr };
- for (ConfigOptionsGroup* opt_group : this->optgroups()) {
- ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1);
+ for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui
+#ifdef _WIN32
+ , m_optgroup_dark_mode
+#endif // _WIN32
+#if ENABLE_ENVIRONMENT_MAP
+ , m_optgroup_render
+#endif // ENABLE_ENVIRONMENT_MAP
+ }) {
+ std::pair<OG_CustomCtrl*, bool*> ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1);
if (ctrl.first && ctrl.second) {
m_highlighter.init(ctrl);
break;
@@ -803,53 +817,5 @@ void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
}
}
-void PreferencesDialog::PreferencesHighlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
-{
- m_timer.SetOwner(owner, timerid);
-}
-
-void PreferencesDialog::PreferencesHighlighter::init(std::pair<OG_CustomCtrl*, bool*> params)
-{
- if (m_timer.IsRunning())
- invalidate();
- if (!params.first || !params.second)
- return;
-
- m_timer.Start(300, false);
-
- m_custom_ctrl = params.first;
- m_show_blink_ptr = params.second;
-
- *m_show_blink_ptr = true;
- m_custom_ctrl->Refresh();
-}
-
-void PreferencesDialog::PreferencesHighlighter::invalidate()
-{
- m_timer.Stop();
-
- if (m_custom_ctrl && m_show_blink_ptr) {
- *m_show_blink_ptr = false;
- m_custom_ctrl->Refresh();
- m_show_blink_ptr = nullptr;
- m_custom_ctrl = nullptr;
- }
-
- m_blink_counter = 0;
-}
-
-void PreferencesDialog::PreferencesHighlighter::blink()
-{
- if (m_custom_ctrl && m_show_blink_ptr) {
- *m_show_blink_ptr = !*m_show_blink_ptr;
- m_custom_ctrl->Refresh();
- }
- else
- return;
-
- if ((++m_blink_counter) == 11)
- invalidate();
-}
-
} // GUI
} // Slic3r
diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp
index 6c8012195..f8b1d1237 100644
--- a/src/slic3r/GUI/Preferences.hpp
+++ b/src/slic3r/GUI/Preferences.hpp
@@ -3,6 +3,7 @@
#include "GUI.hpp"
#include "GUI_Utils.hpp"
+#include "wxExtensions.hpp"
#include <wx/dialog.h>
#include <wx/timer.h>
@@ -10,6 +11,7 @@
#include <map>
class wxColourPickerCtrl;
+class wxBookCtrlBase;
namespace Slic3r {
@@ -39,24 +41,29 @@ class PreferencesDialog : public DPIDialog
wxSizer* m_icon_size_sizer;
wxColourPickerCtrl* m_sys_colour {nullptr};
wxColourPickerCtrl* m_mod_colour {nullptr};
+ wxBookCtrlBase* tabs {nullptr};
+
bool isOSX {false};
bool m_settings_layout_changed {false};
bool m_seq_top_layer_only_changed{ false };
bool m_recreate_GUI{false};
public:
- explicit PreferencesDialog(wxWindow* parent, int selected_tab = 0, const std::string& highlight_opt_key = std::string());
+ explicit PreferencesDialog(wxWindow* paren);
~PreferencesDialog() = default;
bool settings_layout_changed() const { return m_settings_layout_changed; }
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
bool recreate_GUI() const { return m_recreate_GUI; }
- void build(size_t selected_tab = 0);
+ void build();
void update_ctrls_alignment();
void accept(wxEvent&);
+ void show(const std::string& highlight_option = std::string(), const std::string& tab_name = std::string());
protected:
- void on_dpi_changed(const wxRect &suggested_rect) override;
+ void msw_rescale();
+ void on_dpi_changed(const wxRect& suggested_rect) override { msw_rescale(); }
+ void on_sys_color_changed() override;
void layout();
void create_icon_size_slider();
void create_settings_mode_widget();
@@ -64,20 +71,8 @@ protected:
void init_highlighter(const t_config_option_key& opt_key);
std::vector<ConfigOptionsGroup*> optgroups();
- struct PreferencesHighlighter
- {
- void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
- void init(std::pair<OG_CustomCtrl*, bool*>);
- void blink();
- void invalidate();
-
- private:
- OG_CustomCtrl* m_custom_ctrl{ nullptr };
- bool* m_show_blink_ptr{ nullptr };
- int m_blink_counter{ 0 };
- wxTimer m_timer;
- }
- m_highlighter;
+ HighlighterForWx m_highlighter;
+ std::map<std::string, BlinkingBitmap*> m_blinkers;
};
} // GUI
diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp
index add5dc002..bdb2e55bf 100644
--- a/src/slic3r/GUI/Search.cpp
+++ b/src/slic3r/GUI/Search.cpp
@@ -43,6 +43,8 @@ static char marker_by_type(Preset::Type type, PrinterTechnology pt)
return ImGui::MaterialIconMarker;
case Preset::TYPE_PRINTER:
return pt == ptSLA ? ImGui::PrinterSlaIconMarker : ImGui::PrinterIconMarker;
+ case Preset::TYPE_PREFERENCES:
+ return ImGui::PreferencesButton;
default:
return ' ';
}
@@ -302,6 +304,9 @@ void OptionsSearcher::init(std::vector<InputInfo> input_values)
options.clear();
for (auto i : input_values)
append_options(i.config, i.type, i.mode);
+
+ options.insert(options.end(), preferences_options.begin(), preferences_options.end());
+
sort_options();
search(search_line, true);
@@ -323,6 +328,47 @@ void OptionsSearcher::apply(DynamicPrintConfig* config, Preset::Type type, Confi
search(search_line, true);
}
+void OptionsSearcher::append_preferences_option(const GUI::Line& opt_line)
+{
+ Preset::Type type = Preset::TYPE_PREFERENCES;
+ wxString label = opt_line.label;
+ if (label.IsEmpty())
+ return;
+
+ std::string key = get_key(opt_line.get_options().front().opt_id, type);
+ const GroupAndCategory& gc = groups_and_categories[key];
+ if (gc.group.IsEmpty() || gc.category.IsEmpty())
+ return;
+
+ preferences_options.emplace_back(Search::Option{ boost::nowide::widen(key), type,
+ label.ToStdWstring(), _(label).ToStdWstring(),
+ gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
+ gc.category.ToStdWstring(), _(gc.category).ToStdWstring() });
+}
+
+void OptionsSearcher::append_preferences_options(const std::vector<GUI::Line>& opt_lines)
+{
+ //Preset::Type type = Preset::TYPE_PREFERENCES;
+ for (const GUI::Line& line : opt_lines) {
+ if (line.is_separator())
+ continue;
+ append_preferences_option(line);
+ //wxString label = line.label;
+ //if (label.IsEmpty())
+ // continue;
+
+ //std::string key = get_key(line.get_options().front().opt_id, type);
+ //const GroupAndCategory& gc = groups_and_categories[key];
+ //if (gc.group.IsEmpty() || gc.category.IsEmpty())
+ // continue;
+ //
+ //preferences_options.emplace_back(Search::Option{ boost::nowide::widen(key), type,
+ // label.ToStdWstring(), _(label).ToStdWstring(),
+ // gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
+ // gc.category.ToStdWstring(), _(gc.category).ToStdWstring() });
+ }
+}
+
const Option& OptionsSearcher::get_option(size_t pos_in_filter) const
{
assert(pos_in_filter != size_t(-1) && found[pos_in_filter].option_idx != size_t(-1));
@@ -429,6 +475,7 @@ static const std::map<const char, int> icon_idxs = {
{ImGui::PrinterSlaIconMarker, 2},
{ImGui::FilamentIconMarker , 3},
{ImGui::MaterialIconMarker , 4},
+ {ImGui::PreferencesButton , 5},
};
SearchDialog::SearchDialog(OptionsSearcher* searcher)
@@ -717,7 +764,7 @@ void SearchDialog::on_sys_color_changed()
SearchListModel::SearchListModel(wxWindow* parent) : wxDataViewVirtualListModel(0)
{
int icon_id = 0;
- for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin" })
+ for (const std::string& icon : { "cog", "printer", "sla_printer", "spool", "resin", "notification_preferences" })
m_icon[icon_id++] = ScalableBitmap(parent, icon);
}
diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp
index d5add9262..47385e879 100644
--- a/src/slic3r/GUI/Search.hpp
+++ b/src/slic3r/GUI/Search.hpp
@@ -17,6 +17,7 @@
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
+#include "OptionsGroup.hpp"
#include "libslic3r/Preset.hpp"
@@ -85,6 +86,7 @@ class OptionsSearcher
PrinterTechnology printer_technology;
std::vector<Option> options {};
+ std::vector<Option> preferences_options {};
std::vector<FoundOption> found {};
void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
@@ -113,6 +115,8 @@ public:
void apply(DynamicPrintConfig *config,
Preset::Type type,
ConfigOptionMode mode);
+ void append_preferences_option(const GUI::Line& opt_line);
+ void append_preferences_options(const std::vector<GUI::Line>& opt_lines);
bool search();
bool search(const std::string& search, bool force = false);
@@ -197,7 +201,7 @@ protected:
class SearchListModel : public wxDataViewVirtualListModel
{
std::vector<std::pair<wxString, int>> m_values;
- ScalableBitmap m_icon[5];
+ ScalableBitmap m_icon[6];
public:
enum {
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 12c26537c..406617d5a 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -954,7 +954,7 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
{
- auto fit = [this](double s, const Vec3d& offset) {
+ auto fit = [this](double s, Vec3d offset) {
if (s <= 0.0 || s == 1.0)
return;
@@ -972,6 +972,7 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
// center selection on print bed
start_dragging();
+ offset.z() = -get_bounding_box().min.z();
translate(offset);
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp
index 0bfb343b8..e8e23a44e 100644
--- a/src/slic3r/GUI/SendSystemInfoDialog.cpp
+++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp
@@ -556,8 +556,8 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent)
std::string app_name;
{
Semver semver(SLIC3R_VERSION);
- bool is_alpha = std::string{semver.prerelease()}.find("alpha") != std::string::npos;
- bool is_beta = std::string{semver.prerelease()}.find("beta") != std::string::npos;
+ bool is_alpha = semver.prerelease() && std::string{semver.prerelease()}.find("alpha") != std::string::npos;
+ bool is_beta = semver.prerelease() && std::string{semver.prerelease()}.find("beta") != std::string::npos;
app_name = std::string(SLIC3R_APP_NAME) + " " + std::to_string(semver.maj())
+ "." + std::to_string(semver.min()) + " "
+ (is_alpha ? "Alpha" : is_beta ? "Beta" : "");
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 335dd6619..ba7de0cd6 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -51,55 +51,6 @@
namespace Slic3r {
namespace GUI {
-
-void Tab::Highlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
-{
- m_timer.SetOwner(owner, timerid);
-}
-
-void Tab::Highlighter::init(std::pair<OG_CustomCtrl*, bool*> params)
-{
- if (m_timer.IsRunning())
- invalidate();
- if (!params.first || !params.second)
- return;
-
- m_timer.Start(300, false);
-
- m_custom_ctrl = params.first;
- m_show_blink_ptr = params.second;
-
- *m_show_blink_ptr = true;
- m_custom_ctrl->Refresh();
-}
-
-void Tab::Highlighter::invalidate()
-{
- m_timer.Stop();
-
- if (m_custom_ctrl && m_show_blink_ptr) {
- *m_show_blink_ptr = false;
- m_custom_ctrl->Refresh();
- m_show_blink_ptr = nullptr;
- m_custom_ctrl = nullptr;
- }
-
- m_blink_counter = 0;
-}
-
-void Tab::Highlighter::blink()
-{
- if (m_custom_ctrl && m_show_blink_ptr) {
- *m_show_blink_ptr = !*m_show_blink_ptr;
- m_custom_ctrl->Refresh();
- }
- else
- return;
-
- if ((++m_blink_counter) == 11)
- invalidate();
-}
-
Tab::Tab(wxBookCtrlBase* parent, const wxString& title, Preset::Type type) :
m_parent(parent), m_title(title), m_type(type)
{
@@ -134,10 +85,6 @@ Tab::Tab(wxBookCtrlBase* parent, const wxString& title, Preset::Type type) :
}));
m_highlighter.set_timer_owner(this, 0);
- this->Bind(wxEVT_TIMER, [this](wxTimerEvent&)
- {
- m_highlighter.blink();
- });
}
void Tab::set_type()
@@ -289,7 +236,9 @@ void Tab::create_preset_tab()
// There is used just additional sizer for m_mode_sizer with right alignment
if (m_mode_sizer) {
auto mode_sizer = new wxBoxSizer(wxVERTICAL);
- mode_sizer->Add(m_mode_sizer, 1, wxALIGN_RIGHT);
+ // Don't set the 2nd parameter to 1, making the sizer rubbery scalable in Y axis may lead
+ // to wrong vertical size assigned to wxBitmapComboBoxes, see GH issue #7176.
+ mode_sizer->Add(m_mode_sizer, 0, wxALIGN_RIGHT);
m_hsizer->Add(mode_sizer, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, wxOSX ? 15 : 10);
}
@@ -1741,7 +1690,7 @@ void TabPrint::update_description_lines()
if (m_active_page && m_active_page->title() == "Output options" && m_post_process_explanation) {
m_post_process_explanation->SetText(
- _u8L("Post processing scripts shall modify G-code file in place."));
+ _L("Post processing scripts shall modify G-code file in place."));
#ifndef __linux__
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
#endif // __linux__
@@ -1767,7 +1716,9 @@ void TabPrint::update()
// Note: This workaround works till "support_material" and "overhangs" is exclusive sets of mutually no-exclusive parameters.
// But it should be corrected when we will have more such sets.
// Disable check of the compatibility of the "support_material" and "overhangs" options for saved user profile
- if (!m_config_manipulation.is_initialized_support_material_overhangs_queried()) {
+ // NOTE: Initialization of the support_material_overhangs_queried value have to be processed just ones
+ if (!m_config_manipulation.is_initialized_support_material_overhangs_queried())
+ {
const Preset& selected_preset = m_preset_bundle->prints.get_selected_preset();
bool is_user_and_saved_preset = !selected_preset.is_system && !selected_preset.is_dirty;
bool support_material_overhangs_queried = m_config->opt_bool("support_material") && !m_config->opt_bool("overhangs");
@@ -3931,6 +3882,8 @@ bool Tab::validate_custom_gcodes()
bool valid = true;
for (auto opt_group : m_active_page->m_optgroups) {
assert(opt_group->opt_map().size() == 1);
+ if (!opt_group->is_activated())
+ break;
std::string key = opt_group->opt_map().begin()->first;
valid &= validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(opt_group->get_value(key)));
if (!valid)
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 38e142591..45da27bf6 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -218,20 +218,7 @@ protected:
bool m_completed { false };
ConfigOptionMode m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
- struct Highlighter
- {
- void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
- void init(std::pair<OG_CustomCtrl*, bool*>);
- void blink();
- void invalidate();
-
- private:
- OG_CustomCtrl* m_custom_ctrl {nullptr};
- bool* m_show_blink_ptr{nullptr};
- int m_blink_counter {0};
- wxTimer m_timer;
- }
- m_highlighter;
+ HighlighterForWx m_highlighter;
DynamicPrintConfig m_cache_config;
diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp
index 037887ee6..38440b16a 100644
--- a/src/slic3r/GUI/UnsavedChangesDialog.cpp
+++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp
@@ -891,18 +891,14 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_
{
if (!evt.IsChecked())
return;
- wxString preferences_item = m_app_config_key == "default_action_on_new_project" ? _L("Ask for unsaved changes when creating new project") :
+ wxString preferences_item = m_app_config_key == "default_action_on_new_project" ? _L("Ask for unsaved changes when creating new project") :
m_app_config_key == "default_action_on_select_preset" ? _L("Ask for unsaved changes when selecting new preset") :
- _L("Ask for unsaved changes when ??closing application??") ;
+ _L("Ask to save unsaved changes when closing the application or when loading a new project") ;
wxString action = m_app_config_key == "default_action_on_new_project" ? _L("You will not be asked about the unsaved changes the next time you create new project") :
m_app_config_key == "default_action_on_select_preset" ? _L("You will not be asked about the unsaved changes the next time you switch a preset") :
_L("You will not be asked about the unsaved changes the next time you: \n"
- "- close the application,\n"
- "- load project,\n"
- "- process Undo / Redo with a change of print technology,\n"
- "- take/load snapshot,\n"
- "- load config file/bundle,\n"
- "- export config_bundle") ;
+ "- Closing PrusaSlicer while some presets are modified,\n"
+ "- Loading a new project while some presets are modified") ;
wxString msg = _L("PrusaSlicer will remember your action.") + "\n\n" + action + "\n\n" +
format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto be asked about unsaved changes again."), preferences_item);
@@ -1494,7 +1490,7 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe)
});
}
- m_show_all_presets = new wxCheckBox(this, wxID_ANY, _L("Show all preset (including incompatible)"));
+ m_show_all_presets = new wxCheckBox(this, wxID_ANY, _L("Show all presets (including incompatible)"));
m_show_all_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) {
bool show_all = m_show_all_presets->GetValue();
for (auto preset_combos : m_preset_combos) {
@@ -1555,7 +1551,7 @@ void DiffPresetDialog::update_bundles_from_app()
void DiffPresetDialog::show(Preset::Type type /* = Preset::TYPE_INVALID*/)
{
- this->SetTitle(type == Preset::TYPE_INVALID ? _L("Compare Presets") : format_wxstr(_L("Compare %1% Presets"), wxGetApp().get_tab(type)->name()));
+ this->SetTitle(_L("Compare Presets"));
m_view_type = type;
update_bundles_from_app();
diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp
index c76940fd0..f80975ce5 100644
--- a/src/slic3r/GUI/UpdateDialogs.cpp
+++ b/src/slic3r/GUI/UpdateDialogs.cpp
@@ -49,7 +49,7 @@ MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_on
if (dev_version) {
const std::string url = (boost::format(URL_DEV) % ver_online.to_string()).str();
const wxString url_wx = from_u8(url);
- auto *link = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Changelog && Download")), url_wx);
+ auto *link = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Changelog & Download")), url_wx);
content_sizer->Add(link);
} else {
const auto lang_code = wxGetApp().current_language_code_safe().ToStdString();
@@ -132,6 +132,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
line->AddSpacer(3*VERT_SPACING);
line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url));
versions->Add(line);
+ versions->AddSpacer(1); // empty value for the correct alignment inside a GridSizer
}
}
@@ -189,6 +190,7 @@ MsgUpdateForced::MsgUpdateForced(const std::vector<Update>& updates) :
line->AddSpacer(3 * VERT_SPACING);
line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url));
versions->Add(line);
+ versions->AddSpacer(1); // empty value for the correct alignment inside a GridSizer
}
}
diff --git a/src/slic3r/GUI/fts_fuzzy_match.h b/src/slic3r/GUI/fts_fuzzy_match.h
index 379fd9c74..29dd34835 100644
--- a/src/slic3r/GUI/fts_fuzzy_match.h
+++ b/src/slic3r/GUI/fts_fuzzy_match.h
@@ -33,6 +33,7 @@
#include <cstdint> // uint8_t
#include <ctype.h> // ::tolower, ::toupper
+#include <cwctype> // std::towlower, std::towupper
#include <cstring> // memcpy
#include <cstdio>
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index 01a553a81..9b70657e1 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -16,6 +16,7 @@
#include "Plater.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "BitmapComboBox.hpp"
+#include "OG_CustomCtrl.hpp"
#ifndef __linux__
// msw_menuitem_bitmaps is used for MSW and OSX
@@ -979,6 +980,103 @@ void BlinkingBitmap::blink()
this->SetBitmap(show ? bmp.bmp() : wxNullBitmap);
}
+namespace Slic3r {
+namespace GUI {
+
+void Highlighter::set_timer_owner(wxWindow* owner, int timerid/* = wxID_ANY*/)
+{
+ m_timer.SetOwner(owner, timerid);
+ bind_timer(owner);
+}
+
+bool Highlighter::init(bool input_failed)
+{
+ if (input_failed)
+ return false;
+
+ m_timer.Start(300, false);
+ return true;
+}
+void Highlighter::invalidate()
+{
+ if (m_timer.IsRunning())
+ m_timer.Stop();
+ m_blink_counter = 0;
+}
+
+void Highlighter::blink()
+{
+ if ((++m_blink_counter) == 11)
+ invalidate();
+}
+
+// HighlighterForWx
+
+void HighlighterForWx::bind_timer(wxWindow* owner)
+{
+ owner->Bind(wxEVT_TIMER, [this](wxTimerEvent&) {
+ blink();
+ });
+}
+
+// using OG_CustomCtrl where arrow will be rendered and flag indicated "show/hide" state of this arrow
+void HighlighterForWx::init(std::pair<OG_CustomCtrl*, bool*> params)
+{
+ invalidate();
+ if (!Highlighter::init(!params.first && !params.second))
+ return;
+
+ m_custom_ctrl = params.first;
+ m_show_blink_ptr = params.second;
+
+ *m_show_blink_ptr = true;
+ m_custom_ctrl->Refresh();
+}
+
+// - using a BlinkingBitmap. Change state of this bitmap
+void HighlighterForWx::init(BlinkingBitmap* blinking_bmp)
+{
+ invalidate();
+ if (!Highlighter::init(!blinking_bmp))
+ return;
+
+ m_blinking_bitmap = blinking_bmp;
+ m_blinking_bitmap->activate();
+}
+
+void HighlighterForWx::invalidate()
+{
+ Highlighter::invalidate();
+
+ if (m_custom_ctrl && m_show_blink_ptr) {
+ *m_show_blink_ptr = false;
+ m_custom_ctrl->Refresh();
+ m_show_blink_ptr = nullptr;
+ m_custom_ctrl = nullptr;
+ }
+ else if (m_blinking_bitmap) {
+ m_blinking_bitmap->invalidate();
+ m_blinking_bitmap = nullptr;
+ }
+}
+
+void HighlighterForWx::blink()
+{
+ if (m_custom_ctrl && m_show_blink_ptr) {
+ *m_show_blink_ptr = !*m_show_blink_ptr;
+ m_custom_ctrl->Refresh();
+ }
+ else if (m_blinking_bitmap)
+ m_blinking_bitmap->blink();
+ else
+ return;
+
+ Highlighter::blink();
+}
+
+}// GUI
+}//Slicer
+
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index f78621a5c..7e613cff9 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -374,6 +374,67 @@ private:
bool show {false};
};
+// ----------------------------------------------------------------------------
+// Highlighter
+// ----------------------------------------------------------------------------
+
+namespace Slic3r {
+namespace GUI {
+
+class OG_CustomCtrl;
+
+// Highlighter is used as an instrument to put attention to some UI control
+
+class Highlighter
+{
+ int m_blink_counter { 0 };
+ wxTimer m_timer;
+
+public:
+ Highlighter() {}
+ ~Highlighter() {}
+
+ void set_timer_owner(wxWindow* owner, int timerid = wxID_ANY);
+ virtual void bind_timer(wxWindow* owner) = 0;
+ bool init(bool input_failed);
+ void blink();
+ void invalidate();
+};
+
+class HighlighterForWx : public Highlighter
+{
+// There are 2 possible cases to use HighlighterForWx:
+// - using a BlinkingBitmap. Change state of this bitmap
+ BlinkingBitmap* m_blinking_bitmap { nullptr };
+// - using OG_CustomCtrl where arrow will be rendered and flag indicated "show/hide" state of this arrow
+ OG_CustomCtrl* m_custom_ctrl { nullptr };
+ bool* m_show_blink_ptr { nullptr };
+
+public:
+ HighlighterForWx() {}
+ ~HighlighterForWx() {}
+
+ void bind_timer(wxWindow* owner) override;
+ void init(BlinkingBitmap* blinking_bitmap);
+ void init(std::pair<OG_CustomCtrl*, bool*>);
+ void blink();
+ void invalidate();
+};
+/*
+class HighlighterForImGUI : public Highlighter
+{
+
+public:
+ HighlighterForImGUI() {}
+ ~HighlighterForImGUI() {}
+
+ void init();
+ void blink();
+ void invalidate();
+};
+*/
+} // GUI
+} // Slic3r
#endif // slic3r_GUI_wxExtensions_hpp_