diff options
author | Spencer Owen <owenspencer@gmail.com> | 2021-12-08 19:04:20 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-08 19:04:20 +0300 |
commit | 7e453380d69ee47770e526681ac81c62cda0d30b (patch) | |
tree | 419648ec54ee6060b731d0d117f33679423fb333 | |
parent | b81964be0f2ae122738e5fae9fe22ef9711aaaf4 (diff) | |
parent | f697ede19ac93b9dee50c76aa01c9d17a69def19 (diff) |
Merge branch 'prusa3d:master' into ender2-pro
36 files changed, 334 insertions, 241 deletions
diff --git a/build_win.bat b/build_win.bat index a5af27609..1a735d7cd 100644 --- a/build_win.bat +++ b/build_win.bat @@ -6,7 +6,8 @@ @ECHO Performs initial build or rebuild of the app (build) and deps (build/deps).
@ECHO Default options are determined from build directories and system state.
@ECHO.
-@ECHO Usage: build_win [-ARCH ^<arch^>] [-CONFIG ^<config^>] [-DESTDIR ^<directory^>]
+@ECHO Usage: build_win [-ARCH ^<arch^>] [-CONFIG ^<config^>] [-VERSION ^<version^>]
+@ECHO [-PRODUCT ^<product^>] [-DESTDIR ^<directory^>]
@ECHO [-STEPS ^<all^|all-dirty^|app^|app-dirty^|deps^|deps-dirty^>]
@ECHO [-RUN ^<console^|custom^|none^|viewer^|window^>]
@ECHO.
@@ -14,6 +15,10 @@ @ECHO Default: %PS_ARCH_HOST%
@ECHO -c -CONFIG MSVC project config
@ECHO Default: %PS_CONFIG_DEFAULT%
+@ECHO -v -VERSION Major version number of MSVC installation to use for build
+@ECHO Default: %PS_VERSION_SUPPORTED%
+@ECHO -p -PRODUCT Product ID of MSVC installation to use for build
+@ECHO Default: %PS_PRODUCT_DEFAULT%
@ECHO -s -STEPS Performs only the specified build steps:
@ECHO all - clean and build deps and app
@ECHO all-dirty - build deps and app without cleaning
@@ -55,6 +60,23 @@ SET PS_DEPS_PATH_FILE_NAME=.DEPS_PATH.txt SET PS_DEPS_PATH_FILE=%~dp0deps\build\%PS_DEPS_PATH_FILE_NAME%
SET PS_CONFIG_LIST="Debug;MinSizeRel;Release;RelWithDebInfo"
+REM The officially supported toolchain version is 16 (Visual Studio 2019)
+REM TODO: Update versions after Boost gets rolled to 1.78 or later
+SET PS_VERSION_SUPPORTED=16
+SET PS_VERSION_EXCEEDED=17
+SET VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe
+IF NOT EXIST "%VSWHERE%" SET VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe
+FOR /F "tokens=4 USEBACKQ delims=." %%I IN (`"%VSWHERE%" -nologo -property productId`) DO SET PS_PRODUCT_DEFAULT=%%I
+IF "%PS_PRODUCT_DEFAULT%" EQU "" (
+ SET EXIT_STATUS=-1
+ @ECHO ERROR: No Visual Studio installation found. 1>&2
+ GOTO :HELP
+)
+REM Default to the latest supported version if multiple are available
+FOR /F "tokens=1 USEBACKQ delims=." %%I IN (
+ `^""%VSWHERE%" -version "[%PS_VERSION_SUPPORTED%,%PS_VERSION_EXCEEDED%)" -latest -nologo -property catalog_buildVersion^"`
+) DO SET PS_VERSION_SUPPORTED=%%I
+
REM Probe build directories and system state for reasonable default arguments
pushd %~dp0
SET PS_CONFIG=RelWithDebInfo
@@ -62,6 +84,8 @@ SET PS_ARCH=%PROCESSOR_ARCHITECTURE:amd64=x64% CALL :TOLOWER PS_ARCH
SET PS_RUN=none
SET PS_DESTDIR=
+SET PS_VERSION=
+SET PS_PRODUCT=%PS_PRODUCT_DEFAULT%
CALL :RESOLVE_DESTDIR_CACHE
REM Set up parameters used by help menu
@@ -75,7 +99,7 @@ SET EXIT_STATUS=1 SET PS_CURRENT_STEP=arguments
SET PARSER_STATE=
SET PARSER_FAIL=
-FOR %%I in (%*) DO CALL :PARSE_OPTION "ARCH CONFIG DESTDIR STEPS RUN" PARSER_STATE "%%~I"
+FOR %%I in (%*) DO CALL :PARSE_OPTION "ARCH CONFIG DESTDIR STEPS RUN VERSION PRODUCT" PARSER_STATE "%%~I"
IF "%PARSER_FAIL%" NEQ "" (
@ECHO ERROR: Invalid switch: %PARSER_FAIL% 1>&2
GOTO :HELP
@@ -124,6 +148,15 @@ IF "%PS_RUN%" NEQ "none" IF "%PS_STEPS:~0,4%" EQU "deps" ( @ECHO ERROR: RUN=none is the only valid option for STEPS "deps" or "deps-dirty"
GOTO :HELP
)
+IF DEFINED PS_VERSION (
+ SET /A PS_VERSION_EXCEEDED=%PS_VERSION% + 1
+) ELSE SET PS_VERSION=%PS_VERSION_SUPPORTED%
+SET MSVC_FILTER=-products Microsoft.VisualStudio.Product.%PS_PRODUCT% -version "[%PS_VERSION%,%PS_VERSION_EXCEEDED%)"
+FOR /F "tokens=* USEBACKQ" %%I IN (`^""%VSWHERE%" %MSVC_FILTER% -nologo -property installationPath^"`) DO SET MSVC_DIR=%%I
+IF NOT EXIST "%MSVC_DIR%" (
+ @ECHO ERROR: Compatible Visual Studio installation not found. 1>&2
+ GOTO :HELP
+)
REM Give the user a chance to cancel if we found something odd.
IF "%PS_ASK_TO_CONTINUE%" EQU "" GOTO :BUILD_ENV
@ECHO.
@@ -142,9 +175,6 @@ SET PS_CURRENT_STEP=environment @ECHO ** Run App: %PS_RUN%
@ECHO ** Deps path: %PS_DESTDIR%
@ECHO ** Using Microsoft Visual Studio installation found at:
-SET VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe
-IF NOT EXIST "%VSWHERE%" SET VSWHERE=%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe
-FOR /F "tokens=* USEBACKQ" %%I IN (`"%VSWHERE%" -nologo -property installationPath`) DO SET MSVC_DIR=%%I
@ECHO ** %MSVC_DIR%
CALL "%MSVC_DIR%\Common7\Tools\vsdevcmd.bat" -arch=%PS_ARCH% -host_arch=%PS_ARCH_HOST% -app_platform=Desktop
IF %ERRORLEVEL% NEQ 0 GOTO :END
@@ -276,7 +306,7 @@ REM Functions and stubs start here. SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%
mkdir "%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%" > nul 2> nul
REM Copy a legacy file if we don't have one in the proper location.
-echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%"
+echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%" > nul 2> nul
CALL :CANONICALIZE_PATH PS_DEPS_PATH_FILE_FOR_CONFIG
IF EXIST "%PS_DEPS_PATH_FILE_FOR_CONFIG%" (
FOR /F "tokens=* USEBACKQ" %%I IN ("%PS_DEPS_PATH_FILE_FOR_CONFIG%") DO (
diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 73d841014..62222f812 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,7 +13,7 @@ prusaslicer_add_cmake_project(wxWidgets # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" # GIT_TAG tm_cross_compile #${_wx_git_tag} URL https://github.com/prusa3d/wxWidgets/archive/refs/heads/v3.1.4-patched.zip - URL_HASH SHA256=ed36a2159c781cce07b06378664e683ebd8cb2f51914aba9acd3bfca3d63d7d3 + URL_HASH SHA256=78adc312e645d738945172d5ddcee16b1a55cca08e82b43379192262377a206a DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG CMAKE_ARGS -DwxBUILD_PRECOMP=ON diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 6d082a431..f74775a9a 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -498,7 +498,7 @@ Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_pos else if (seam_position == spRear) { // Object is centered around (0,0) in its current coordinate system. last_pos.x() = 0; - last_pos.y() += coord_t(3. * po->bounding_box().radius()); + last_pos.y() = coord_t(3. * po->bounding_box().radius()); last_pos_weight = 5.f; } if (seam_position == spNearest) { // last_pos already contains current nozzle position diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 5c661ed68..d273fde96 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -92,6 +92,25 @@ void Layer::restore_untyped_slices() } } +// Similar to Layer::restore_untyped_slices() +// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. +// Only resetting layerm->slices if Slice::extra_perimeters is always zero or it will not be used anymore +// after the perimeter generator. +void Layer::restore_untyped_slices_no_extra_perimeters() +{ + if (layer_needs_raw_backup(this)) { + for (LayerRegion *layerm : m_regions) + if (! layerm->region().config().extra_perimeters.value) + layerm->slices.set(layerm->raw_slices, stInternal); + } else { + assert(m_regions.size() == 1); + LayerRegion *layerm = m_regions.front(); + // This optimization is correct, as extra_perimeters are only reused by prepare_infill() with multi-regions. + //if (! layerm->region().config().extra_perimeters.value) + layerm->slices.set(this->lslices, stInternal); + } +} + ExPolygons Layer::merged(float offset_scaled) const { assert(offset_scaled >= 0.f); @@ -179,7 +198,7 @@ void Layer::make_perimeters() // group slices (surfaces) according to number of extra perimeters std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ] for (LayerRegion *layerm : layerms) { - for (Surface &surface : layerm->slices.surfaces) + for (const Surface &surface : layerm->slices.surfaces) slices[surface.extra_perimeters].emplace_back(surface); if (layerm->region().config().fill_density > layerm_config->region().config().fill_density) layerm_config = layerm; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 516b6da9b..0071c7f6e 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -137,6 +137,8 @@ public: //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. void backup_untyped_slices(); void restore_untyped_slices(); + // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. + void restore_untyped_slices_no_extra_perimeters(); // Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices. ExPolygons merged(float offset) const; template <class T> bool any_internal_region_slice_contains(const T &item) const { diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 4dbffe7b0..fd29d6d54 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -51,8 +51,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() // so we're safe. This guarantees idempotence of prepare_infill() also in case // that combine_infill() turns some fill_surface into VOID surfaces. // Collect polygons per surface type. - std::vector<SurfacesPtr> by_surface; - by_surface.assign(size_t(stCount), SurfacesPtr()); + std::array<SurfacesPtr, size_t(stCount)> by_surface; for (Surface &surface : this->slices.surfaces) by_surface[size_t(surface.surface_type)].emplace_back(&surface); // Trim surfaces by the fill_boundaries. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 011539aa4..b2e645714 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1210,6 +1210,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " "bottom layer to improve adhesion and tolerance for non perfect build plates."); def->sidetext = L("mm"); + def->min = 0; def->ratio_over = "layer_height"; def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false)); @@ -3772,7 +3773,7 @@ void PrintConfigDef::init_sla_params() def->enum_labels.push_back(L("Slow")); def->enum_labels.push_back(L("Fast")); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum<SLAMaterialSpeed>(slamsSlow)); + def->set_default_value(new ConfigOptionEnum<SLAMaterialSpeed>(slamsFast)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eee0c2aba..05b8c9eb6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "I18N.hpp" #include "Layer.hpp" +#include "MutablePolygon.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" @@ -226,6 +227,17 @@ void PrintObject::prepare_infill() m_print->set_status(30, L("Preparing infill")); + if (m_typed_slices) { + // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. + // The preceding step (perimeter generator) only modifies extra_perimeters and the extra perimeters are only used by discover_vertical_shells() + // with more than a single region. If this step does not use Surface::extra_perimeters or Surface::extra_perimeters is always zero, it is safe + // to reset to the untyped slices before re-runnning detect_surfaces_type(). + for (Layer* layer : m_layers) { + layer->restore_untyped_slices_no_extra_perimeters(); + m_print->throw_if_canceled(); + } + } + // This will assign a type (top/bottom/internal) to $layerm->slices. // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces @@ -1709,9 +1721,6 @@ void PrintObject::clip_fill_surfaces() Layer *layer = m_layers[layer_id]; Layer *lower_layer = m_layers[layer_id - 1]; // Detect things that we need to support. - // Cummulative slices. - Polygons slices; - polygons_append(slices, layer->lslices); // Cummulative fill surfaces. Polygons fill_surfaces; // Solid surfaces to be supported. @@ -1736,7 +1745,7 @@ void PrintObject::clip_fill_surfaces() { // Get perimeters area as the difference between slices and fill_surfaces // Only consider the area that is not supported by lower perimeters - Polygons perimeters = intersection(diff(slices, fill_surfaces), lower_layer_fill_surfaces); + Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); // Only consider perimeter areas that are at least one extrusion width thick. //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. //Should the pw not be half of the current value? @@ -1746,9 +1755,15 @@ void PrintObject::clip_fill_surfaces() // Append such thick perimeters to the areas that need support polygons_append(overhangs, opening(perimeters, pw)); } - // Find new internal infill. - polygons_append(overhangs, std::move(upper_internal)); - upper_internal = intersection(overhangs, lower_layer_internal_surfaces); + // Merge the new overhangs, find new internal infill. + polygons_append(upper_internal, std::move(overhangs)); + static constexpr const auto closing_radius = scaled<float>(2.f); + upper_internal = intersection( + // Regularize the overhang regions, so that the infill areas will not become excessively jagged. + smooth_outward( + closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), + scaled<coord_t>(0.1)), + lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { if (layerm->region().config().fill_density.value == 0) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 0b2af37b8..fe108b1a6 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -47,8 +47,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->opt_float("layer_height") < EPSILON) { const wxString msg_text = _(L("Layer height is not valid.\n\nThe layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -60,8 +59,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con if (config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value < EPSILON) { const wxString msg_text = _(L("First layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; is_msg_dlg_already_exist = true; dialog.ShowModal(); @@ -90,8 +88,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "- Detect thin walls disabled")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), - MessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Spiral Vase")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -126,8 +123,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "(both support_material_extruder and support_material_interface_extruder need to be set to 0).")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable the Wipe Tower?")); - //wxMessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog (nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog (m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -147,8 +143,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con "need to be synchronized with the object layers.")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I synchronize support layers in order to enable the Wipe Tower?")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), - MessageDialog dialog(nullptr, msg_text, _(L("Wipe Tower")), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Wipe Tower")), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -169,7 +164,7 @@ 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(nullptr, 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 | (is_global_config ? wxYES | wxNO : wxOK)); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); if (!is_global_config || answer == wxID_YES) { @@ -200,8 +195,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con _(fill_pattern_def->enum_labels[it_pattern - fill_pattern_def->enum_values.begin()])); if (is_global_config) msg_text += "\n\n" + _L("Shall I switch to rectilinear fill pattern?"); - //wxMessageDialog dialog(nullptr, msg_text, _L("Infill"), - MessageDialog dialog(nullptr, msg_text, _L("Infill"), + MessageDialog dialog(m_msg_dlg_parent, msg_text, _L("Infill"), wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK) ); DynamicPrintConfig new_conf = *config; auto answer = dialog.ShowModal(); @@ -331,8 +325,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (head_penetration > head_width) { wxString msg_text = _(L("Head penetration should not be greater than the head width.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width)); @@ -345,8 +338,7 @@ void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, con if (pinhead_d > pillar_d) { wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter.")); - //wxMessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); - MessageDialog dialog(nullptr, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); + MessageDialog dialog(m_msg_dlg_parent, msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); DynamicPrintConfig new_conf = *config; if (dialog.ShowModal() == wxID_OK) { diff --git a/src/slic3r/GUI/ConfigManipulation.hpp b/src/slic3r/GUI/ConfigManipulation.hpp index 0e6815753..32ddb52a9 100644 --- a/src/slic3r/GUI/ConfigManipulation.hpp +++ b/src/slic3r/GUI/ConfigManipulation.hpp @@ -30,15 +30,18 @@ class ConfigManipulation // callback to propagation of changed value, if needed std::function<void(const std::string&, const boost::any&)> cb_value_change = nullptr; ModelConfig* local_config = nullptr; + wxWindow* m_msg_dlg_parent {nullptr}; public: ConfigManipulation(std::function<void()> load_config, std::function<void(const std::string&, bool toggle, int opt_index)> cb_toggle_field, std::function<void(const std::string&, const boost::any&)> cb_value_change, - ModelConfig* local_config = nullptr) : + ModelConfig* local_config = nullptr, + wxWindow* msg_dlg_parent = nullptr) : load_config(load_config), cb_toggle_field(cb_toggle_field), cb_value_change(cb_value_change), + m_msg_dlg_parent(msg_dlg_parent), local_config(local_config) {} ConfigManipulation() {} diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 67f78d26e..5ec622b87 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -291,6 +291,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true case coString: case coStrings: case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && m_opt.opt_key == "first_layer_height" && !str.IsEmpty() && str.Last() == '%') { + // Workaroud to avoid of using of the % for first layer height + // see https://github.com/prusa3d/PrusaSlicer/issues/7418 + wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); + show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); + const wxString stVal = double_to_string(0.01, 2); + set_value(stVal, true); + m_value = into_u8(stVal);; + break; + } if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') { double val = 0.; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index cdc9c33a4..574ba6ec5 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -977,8 +977,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const for (const RenderPath& path : t_buffer.render_paths) { colors.push_back(path.color); } - std::sort(colors.begin(), colors.end()); - colors.erase(std::unique(colors.begin(), colors.end()), colors.end()); + sort_remove_duplicates(colors); // save materials file boost::filesystem::path mat_filename(filename); @@ -1447,7 +1446,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) static const unsigned int progress_threshold = 1000; wxProgressDialog* progress_dialog = wxGetApp().is_gcode_viewer() ? new wxProgressDialog(_L("Generating toolpaths"), "...", - 100, wxGetApp().plater(), wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; + 100, wxGetApp().mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL) : nullptr; wxBusyCursor busy; @@ -2020,13 +2019,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) } // roles -> remove duplicates - std::sort(m_roles.begin(), m_roles.end()); - m_roles.erase(std::unique(m_roles.begin(), m_roles.end()), m_roles.end()); + sort_remove_duplicates(m_roles); m_roles.shrink_to_fit(); // extruder ids -> remove duplicates - std::sort(m_extruder_ids.begin(), m_extruder_ids.end()); - m_extruder_ids.erase(std::unique(m_extruder_ids.begin(), m_extruder_ids.end()), m_extruder_ids.end()); + sort_remove_duplicates(m_extruder_ids); m_extruder_ids.shrink_to_fit(); // set layers z range @@ -2374,8 +2371,10 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } RenderPath key{ tbuffer_id, color, static_cast<unsigned int>(ibuffer_id), path_id }; - if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) - render_path = const_cast<RenderPath*>(&(*buffer.render_paths.emplace(key).first)); + if (render_path == nullptr || !RenderPathPropertyEqual()(*render_path, key)) { + buffer.render_paths.emplace_back(key); + render_path = const_cast<RenderPath*>(&buffer.render_paths.back()); + } unsigned int delta_1st = 0; if (sub_path.first.s_id < m_sequential_view.current.first && m_sequential_view.current.first <= sub_path.last.s_id) @@ -2433,6 +2432,14 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool #endif } + // Removes empty render paths and sort. + for (size_t b = 0; b < m_buffers.size(); ++b) { + TBuffer* buffer = const_cast<TBuffer*>(&m_buffers[b]); + buffer->render_paths.erase(std::remove_if(buffer->render_paths.begin(), buffer->render_paths.end(), + [](const auto &path){ return path.sizes.empty() || path.offsets.empty(); }), + buffer->render_paths.end()); + } + // second pass: for buffers using instanced and batched models, update the instances render ranges for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer& buffer = const_cast<TBuffer&>(m_buffers[b]); @@ -2624,12 +2631,7 @@ void GCodeViewer::render_toolpaths() float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast<float>(viewport[3]) / (2.0f * static_cast<float>(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) : static_cast<float>(viewport[3]) * 0.0005; -#if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_points = [this, zoom, point_size, near_plane_height] -#else - auto render_as_points = [zoom, point_size, near_plane_height] -#endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { + auto shader_init_as_points = [zoom, point_size, near_plane_height](GLShaderProgram& shader) { #if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS shader.set_uniform("use_fixed_screen_size", 1); #else @@ -2640,65 +2642,67 @@ void GCodeViewer::render_toolpaths() shader.set_uniform("percent_center_radius", 0.33f); shader.set_uniform("point_size", point_size); shader.set_uniform("near_plane_height", near_plane_height); + }; + auto render_as_points = [ +#if ENABLE_GCODE_VIEWER_STATISTICS + this +#endif // ENABLE_GCODE_VIEWER_STATISTICS + ](std::vector<RenderPath>::iterator it_path, std::vector<RenderPath>::iterator it_end, GLShaderProgram& shader, int uniform_color) { glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)); glsafe(::glEnable(GL_POINT_SPRITE)); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - 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())); + 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()))); + 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; + ++m_statistics.gl_multi_points_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } glsafe(::glDisable(GL_POINT_SPRITE)); glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE)); }; + auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) { + shader.set_uniform("light_intensity", light_intensity); + }; + auto render_as_lines = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_lines = [this, light_intensity] -#else - auto render_as_lines = [light_intensity] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - shader.set_uniform("light_intensity", light_intensity); - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - 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())); + ](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()))); + 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; + ++m_statistics.gl_multi_lines_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_triangles = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_triangles = [this] -#else - auto render_as_triangles = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (const TBuffer& buffer, unsigned int ibuffer_id, GLShaderProgram& shader) { - for (const RenderPath& path : buffer.render_paths) { - if (path.ibuffer_id == ibuffer_id) { - 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())); + ](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()))); + 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; + ++m_statistics.gl_multi_triangles_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS - } } }; + auto render_as_instanced_model = [ #if ENABLE_GCODE_VIEWER_STATISTICS - auto render_as_instanced_model = [this] -#else - auto render_as_instanced_model = [] + this #endif // ENABLE_GCODE_VIEWER_STATISTICS - (TBuffer& buffer, GLShaderProgram & shader) { + ](TBuffer& buffer, GLShaderProgram & shader) { for (auto& range : buffer.model.instances.render_ranges.ranges) { if (range.vbo == 0 && range.count > 0) { glsafe(::glGenBuffers(1, &range.vbo)); @@ -2803,8 +2807,20 @@ void GCodeViewer::render_toolpaths() shader->set_uniform("emission_factor", 0.0f); } else { - for (size_t j = 0; j < buffer.indices.size(); ++j) { - const IBuffer& i_buffer = buffer.indices[j]; + switch (buffer.render_primitive_type) { + case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break; + case TBuffer::ERenderPrimitiveType::Line: shader_init_as_lines(*shader); break; + default: break; + } + int uniform_color = shader->get_uniform_location("uniform_color"); + auto it_path = buffer.render_paths.begin(); + for (unsigned int ibuffer_id = 0; ibuffer_id < static_cast<unsigned int>(buffer.indices.size()); ++ibuffer_id) { + const IBuffer& i_buffer = buffer.indices[ibuffer_id]; + // Skip all paths with ibuffer_id < ibuffer_id. + for (; it_path != buffer.render_paths.end() && it_path->ibuffer_id < ibuffer_id; ++ it_path) ; + if (it_path == buffer.render_paths.end() || it_path->ibuffer_id > ibuffer_id) + // Not found. This shall not happen. + continue; glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); @@ -2817,19 +2833,20 @@ void GCodeViewer::render_toolpaths() glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. switch (buffer.render_primitive_type) { case TBuffer::ERenderPrimitiveType::Point: { - render_as_points(buffer, static_cast<unsigned int>(j), *shader); + render_as_points(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Line: { glsafe(::glLineWidth(static_cast<GLfloat>(line_width(zoom)))); - render_as_lines(buffer, static_cast<unsigned int>(j), *shader); + render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } case TBuffer::ERenderPrimitiveType::Triangle: { - render_as_triangles(buffer, static_cast<unsigned int>(j), *shader); + render_as_triangles(it_path, buffer.render_paths.end(), *shader, uniform_color); break; } default: { break; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 9147eec84..c56e88c88 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -259,14 +259,6 @@ class GCodeViewer return false; } }; -// // for unordered_set implementation of render_paths -// struct RenderPathPropertyHash { -// size_t operator() (const RenderPath &p) const { -// // Convert the RGB value to an integer hash. -//// return (size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) * 7919) ^ size_t(p.ibuffer_id); -// return size_t(int(p.color[0] * 255) + 255 * int(p.color[1] * 255) + (255 * 255) * int(p.color[2] * 255)) ^ size_t(p.ibuffer_id); -// } -// }; struct RenderPathPropertyLower { bool operator() (const RenderPath &l, const RenderPath &r) const { if (l.tbuffer_id < r.tbuffer_id) @@ -319,9 +311,7 @@ class GCodeViewer std::string shader; std::vector<Path> paths; - // std::set seems to perform significantly better, at least on Windows. -// std::unordered_set<RenderPath, RenderPathPropertyHash, RenderPathPropertyEqual> render_paths; - std::set<RenderPath, RenderPathPropertyLower> render_paths; + std::vector<RenderPath> render_paths; bool visible{ false }; void reset(); diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 0473e5344..9c1e93652 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -358,12 +358,38 @@ bool GLShaderProgram::set_uniform(const char* name, const Vec3d& value) const int GLShaderProgram::get_attrib_location(const char* name) const { - return (m_id > 0) ? ::glGetAttribLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_attrib_location_cache.begin(), m_attrib_location_cache.end(), [name](const auto& p) { return p.first == name; }); + if (it != m_attrib_location_cache.end()) + // Attrib ID cached. + return it->second; + + int id = ::glGetAttribLocation(m_id, name); + const_cast<GLShaderProgram*>(this)->m_attrib_location_cache.push_back({ name, id }); + return id; } int GLShaderProgram::get_uniform_location(const char* name) const { - return (m_id > 0) ? ::glGetUniformLocation(m_id, name) : -1; + assert(m_id > 0); + + if (m_id <= 0) + // Shader program not loaded. This should not happen. + return -1; + + auto it = std::find_if(m_uniform_location_cache.begin(), m_uniform_location_cache.end(), [name](const auto &p) { return p.first == name; }); + if (it != m_uniform_location_cache.end()) + // Uniform ID cached. + return it->second; + + int id = ::glGetUniformLocation(m_id, name); + const_cast<GLShaderProgram*>(this)->m_uniform_location_cache.push_back({ name, id }); + return id; } } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index c92ea274a..d7b92000d 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -29,6 +29,8 @@ public: private: std::string m_name; unsigned int m_id{ 0 }; + std::vector<std::pair<std::string, int>> m_attrib_location_cache; + std::vector<std::pair<std::string, int>> m_uniform_location_cache; public: ~GLShaderProgram(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c7654fcd6..bda7394ed 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -883,6 +883,8 @@ void GUI_App::init_app_config() dir = wxFileName::GetHomeDir() + wxS("/.config"); set_data_dir((dir + "/" + GetAppName()).ToUTF8().data()); #endif + } else { + m_datadir_redefined = true; } if (!app_config) @@ -915,6 +917,10 @@ void GUI_App::init_app_config() // returns true if found newer version and user agreed to use it bool GUI_App::check_older_app_config(Semver current_version, bool backup) { + // If the config folder is redefined - do not check + if (m_datadir_redefined) + return false; + // find other version app config (alpha / beta / release) std::string config_path = app_config->config_path(); boost::filesystem::path parent_file_path(config_path); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 82feb3282..b1b0a7786 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -352,6 +352,7 @@ private: 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; }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7d689ff6a..f4d27b0b2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1446,7 +1446,7 @@ void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& else wxGetApp().import_model(parent, input_files); - wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); + wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().mainframe wxPD_AUTO_HIDE); wxBusyCursor busy; for (size_t i = 0; i < input_files.size(); ++i) { @@ -1506,7 +1506,7 @@ void ObjectList::load_modifier(ModelObject& model_object, std::vector<ModelVolum else wxGetApp().import_model(parent, input_files); - wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); + wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().mainframe, wxPD_AUTO_HIDE); wxBusyCursor busy; const int obj_idx = get_selected_obj_idx(); @@ -4105,7 +4105,7 @@ void ObjectList::fix_through_netfabb() Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb")); // Open a progress dialog. - wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, plater, + wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); int model_idx{ 0 }; if (vol_idxs.empty()) { diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index c92ddd3b5..c9a7cd330 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -110,12 +110,6 @@ bool ObjectSettings::update_settings_list() update_settings_list(); m_parent->Layout(); }); - - /* Check overriden options list after deleting. - * Some options couldn't be deleted because of another one. - * Like, we couldn't delete fill pattern, if fill density is set to 100% - */ - update_config_values(config); }); return btn; }; @@ -227,11 +221,12 @@ void ObjectSettings::update_config_values(ModelConfig* config) update_config_values(config); if (is_added) { - wxTheApp->CallAfter([this]() { +// #ysFIXME - Delete after testing! Very likely this CallAfret is no needed +// wxTheApp->CallAfter([this]() { wxWindowUpdateLocker noUpdates(m_parent); update_settings_list(); m_parent->Layout(); - }); +// }); } }; @@ -253,9 +248,9 @@ void ObjectSettings::update_config_values(ModelConfig* config) { const int obj_idx = objects_model->GetObjectIdByItem(item); assert(obj_idx >= 0); + // for object's part first of all update konfiguration from object main_config.apply(wxGetApp().model().objects[obj_idx]->config.get(), true); - printer_technology == ptFFF ? config_manipulation.update_print_fff_config(&main_config) : - config_manipulation.update_print_sla_config(&main_config) ; + // and then from its own config } main_config.apply(config->get(), true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 614e83811..691a86706 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -550,24 +550,24 @@ RENDER_AGAIN: ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - settings_sliders_left); m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + 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 + bool slider_released =m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider if (current_mode >= quality_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("quality")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; } if (current_mode >= closing_d_mode) { @@ -575,12 +575,12 @@ RENDER_AGAIN: m_imgui->text(m_desc.at("closing_distance")); ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); - if (ImGui::IsItemHovered()) + if (m_imgui->get_last_slider_status().hovered) m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; } if (slider_clicked) { @@ -627,9 +627,9 @@ RENDER_AGAIN: //complete non-sense. diam = std::clamp(diam, 0.1f, diameter_upper_cap); m_new_hole_radius = diam / 2.f; - bool clicked = ImGui::IsItemClicked(); - bool edited = ImGui::IsItemEdited(); - bool deactivated = ImGui::IsItemDeactivatedAfterEdit(); + bool clicked = m_imgui->get_last_slider_status().clicked; + bool edited = m_imgui->get_last_slider_status().edited; + bool deactivated = m_imgui->get_last_slider_status().deactivated_after_edit; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc["hole_depth"]); @@ -638,9 +638,9 @@ RENDER_AGAIN: // Same as above: m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); - clicked |= ImGui::IsItemClicked(); - edited |= ImGui::IsItemEdited(); - deactivated |= ImGui::IsItemDeactivatedAfterEdit(); + clicked |= m_imgui->get_last_slider_status().clicked; + edited |= m_imgui->get_last_slider_status().edited; + deactivated |= m_imgui->get_last_slider_status().deactivated_after_edit;; // Following is a nasty way to: // - save the initial value of the slider before one starts messing with it diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index e164caee6..8b866c7c9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -859,9 +859,9 @@ void GLPaintContour::finalize_geometry() if (!this->contour_indices.empty()) { glsafe(::glGenBuffers(1, &this->m_contour_EBO_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->m_contour_EBO_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_contour_EBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->contour_indices.size() * sizeof(unsigned int), this->contour_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); this->contour_indices.clear(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 9170db603..29e1fd2f3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -687,16 +687,16 @@ RENDER_AGAIN: // - take correct undo/redo snapshot after the user is done with moving the slider float initial_value = m_new_point_head_diameter; m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); - if (ImGui::IsItemClicked()) { + if (m_imgui->get_last_slider_status().clicked) { if (m_old_point_head_diameter == 0.f) m_old_point_head_diameter = initial_value; } - if (ImGui::IsItemEdited()) { + if (m_imgui->get_last_slider_status().edited) { for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; } - if (ImGui::IsItemDeactivatedAfterEdit()) { + if (m_imgui->get_last_slider_status().deactivated_after_edit) { // momentarily restore the old value to take snapshot for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) @@ -747,18 +747,18 @@ RENDER_AGAIN: float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value; m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + 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 + bool slider_released = m_imgui->get_last_slider_status().deactivated_after_edit; // someone has just released the slider ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("points_density")); ImGui::SameLine(settings_sliders_left); m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + slider_clicked |= m_imgui->get_last_slider_status().clicked; + slider_edited |= m_imgui->get_last_slider_status().edited; + slider_released |= m_imgui->get_last_slider_status().deactivated_after_edit; if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo m_minimal_point_distance_stash = minimal_point_distance; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 325067aea..a01e2ed69 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -503,6 +503,12 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float str_label = str_label.substr(0, pos) + str_label.substr(pos + 2); bool ret = ImGui::SliderFloat(str_label.c_str(), v, v_min, v_max, format, power); + + m_last_slider_status.hovered = ImGui::IsItemHovered(); + m_last_slider_status.edited = ImGui::IsItemEdited(); + m_last_slider_status.clicked = ImGui::IsItemClicked(); + m_last_slider_status.deactivated_after_edit = ImGui::IsItemDeactivatedAfterEdit(); + if (!tooltip.empty() && ImGui::IsItemHovered()) this->tooltip(into_u8(tooltip).c_str(), max_tooltip_width); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 590ef95b0..255c4171b 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -39,6 +39,13 @@ class ImGuiWrapper std::string m_clipboard_text; public: + struct LastSliderStatus { + bool hovered { false }; + bool edited { false }; + bool clicked { false }; + bool deactivated_after_edit { false }; + }; + ImGuiWrapper(); ~ImGuiWrapper(); @@ -63,6 +70,7 @@ public: ImVec2 get_item_spacing() const; float get_slider_float_height() const; + const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); @@ -146,6 +154,8 @@ private: static const char* clipboard_get(void* user_data); static void clipboard_set(void* user_data, const char* text); + + LastSliderStatus m_last_slider_status; }; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 32061fd32..8a9702c40 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2101,21 +2101,6 @@ void MainFrame::technology_changed() } -#if defined(__linux__) || defined(_WIN32) -// wxWidgets callback to enable / disable window and all its children windows. -// called by wxWindowDisabler when entering / leaving modal dialog loop. -// Unfortunately the wxWindowDisabler calls Enable(true) after the wxEVT_ACTIVATE event is processed -// while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails -// and we need to do it now. -bool MainFrame::Enable(bool enable) -{ - bool retval = DPIFrame::Enable(enable); - if (enable && retval) - this->plater()->restore_keyboard_focus(); - return retval; -} -#endif - // // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 9e09bf088..951ed70a1 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -148,15 +148,6 @@ public: void update_title(); -#if defined(__linux__) || defined(_WIN32) - // wxWidgets callback to enable / disable window and all its children windows. - // called by wxWindowDisabler when entering / leaving modal dialog loop. - // Unfortunately the wxWindowDisabler calls Enable(true) after the wxEVT_ACTIVATE event is processed - // while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails - // and we need to do it now. - bool Enable(bool enable = true) override; -#endif - void init_tabpanel(); void create_preset_tabs(); void add_created_tab(Tab* panel, const std::string& bmp_name = ""); @@ -211,7 +202,7 @@ public: SettingsDialog m_settings_dialog; DiffPresetDialog diff_dialog; wxWindow* m_plater_page{ nullptr }; - wxProgressDialog* m_progress_dialog { nullptr }; +// wxProgressDialog* m_progress_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 a505c2507..56e990577 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -136,7 +136,8 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin int em = wxGetApp().em_unit(); // if message containes the table - if (msg.Contains("<tr>")) { + bool is_marked = msg.Contains("<tr>"); + if (is_marked) { int lines = msg.Freq('\n') + 1; int pos = 0; while (pos < (int)msg.Len() && pos != wxNOT_FOUND) { @@ -154,7 +155,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()); + std::string msg_escaped = xml_escape(msg.ToUTF8().data(), is_marked); boost::replace_all(msg_escaped, "\r\n", "<br>"); boost::replace_all(msg_escaped, "\n", "<br>"); if (monospaced_font) diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index e9153c70f..ee19e5da5 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -79,7 +79,6 @@ void OG_CustomCtrl::init_ctrl_lines() // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && opt_group->label_width == 0 && option_set.front().opt.full_width && - option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { @@ -157,7 +156,6 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) // If we have a single option with no sidetext const std::vector<Option>& option_set = line.get_options(); if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && - option_set.front().opt.label.empty() && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { h_pos += 3 * blinking_button_width; @@ -167,13 +165,14 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) break; } + bool is_multioption_line = option_set.size() > 1; for (auto opt : option_set) { Field* field = opt_group->get_field(opt.opt_id); correct_line_height(ctrl_line.height, field->getWindow()); ConfigOptionDef option = opt.opt; // add label if any - if (!option.label.empty()) { + if (is_multioption_line && !option.label.empty()) { //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _CTX(option.label, "Layers") : _(option.label); @@ -581,7 +580,6 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) // If we have a single option with no sidetext just add it directly to the grid sizer if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && - option_set.front().opt.label.empty() && option_set.front().side_widget == nullptr && og_line.get_extra_widgets().size() == 0) { if (field && field->undo_to_sys_bitmap()) @@ -595,11 +593,12 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) } size_t bmp_rect_id = 0; + bool is_multioption_line = option_set.size() > 1; for (const Option& opt : option_set) { field = ctrl->opt_group->get_field(opt.opt_id); ConfigOptionDef option = opt.opt; // add label if any - if (!option.label.empty()) { + if (is_multioption_line && !option.label.empty()) { //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _CTX(option.label, "Layers") : _(option.label); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 23c9051be..6039dcbcb 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -2,11 +2,13 @@ #include "ConfigExceptions.hpp" #include "Plater.hpp" #include "GUI_App.hpp" +#include "MainFrame.hpp" #include "OG_CustomCtrl.hpp" #include "MsgDialog.hpp" #include "format.hpp" #include <utility> +#include <wx/bookctrl.h> #include <wx/numformatter.h> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> @@ -247,7 +249,6 @@ void OptionsGroup::activate_line(Line& line) // if we have a single option with no label, no sidetext just add it directly to sizer if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && - option_set.front().opt.label.empty() && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { @@ -326,7 +327,6 @@ void OptionsGroup::activate_line(Line& line) grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); // If we have a single option with no sidetext just add it directly to the grid sizer if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && - option_set.front().opt.label.empty() && option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { const auto& option = option_set.front(); const auto& field = build_field(option); @@ -341,11 +341,12 @@ void OptionsGroup::activate_line(Line& line) return; } + bool is_multioption_line = option_set.size() > 1; for (auto opt : option_set) { ConfigOptionDef option = opt.opt; wxSizer* sizer_tmp = sizer; // add label if any - if (!option.label.empty() && !custom_ctrl) { + if ((is_multioption_line || line.label.IsEmpty()) && !option.label.empty() && !custom_ctrl) { //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ? _CTX(option.label, "Layers") : @@ -507,15 +508,13 @@ void OptionsGroup::clear(bool destroy_custom_ctrl) m_fields.clear(); } -Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const { -// Line retval{ _(option.opt.label), _(option.opt.tooltip) }; +Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const +{ wxString tooltip = _(option.opt.tooltip); edit_tooltip(tooltip); Line retval{ _(option.opt.label), tooltip }; retval.label_path = path; - Option tmp(option); - tmp.opt.label = std::string(""); - retval.append_option(tmp); + retval.append_option(option); return retval; } @@ -981,7 +980,8 @@ bool OptionsGroup::launch_browser(const std::string& path_end) bool launch = true; if (get_app_config()->get("suppress_hyperlinks").empty()) { - RichMessageDialog dialog(nullptr, _L("Open hyperlink in default browser?"), _L("PrusaSlicer: Open hyperlink"), wxYES_NO); + wxWindow* parent = wxGetApp().mainframe->m_tabpanel; + 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(); @@ -992,7 +992,7 @@ bool OptionsGroup::launch_browser(const std::string& path_end) _L("You will not be asked about it again on label hovering.") + "\n\n" + format_wxstr(_L("Visit \"Preferences\" and check \"%1%\"\nto changes your choice."), preferences_item); - MessageDialog msg_dlg(nullptr, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); + MessageDialog msg_dlg(parent, msg, _L("PrusaSlicer: Don't ask me again"), wxOK | wxCANCEL | wxICON_INFORMATION); if (msg_dlg.ShowModal() == wxID_CANCEL) return false; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index dd1ccb91f..0485b0075 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2351,7 +2351,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ } const auto loading = _L("Loading") + dots; - wxProgressDialog dlg(loading, "", 100, q, wxPD_AUTO_HIDE); + wxProgressDialog dlg(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE); wxBusyCursor busy; auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model(); @@ -6306,36 +6306,9 @@ void Plater::force_print_bed_update() void Plater::on_activate() { -#if defined(__linux__) || defined(_WIN32) - this->restore_keyboard_focus(); -#endif this->p->show_delayed_error_message(); } -#if defined(__linux__) || defined(_WIN32) -// wxWidgets callback to enable / disable window and all its children windows. -// called by wxProgressDialog when entering / leaving modal dialog loop. -// Unfortunately the wxProgressDialog calls Enable(true) after the wxEVT_ACTIVATE event is processed -// while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails -// and we need to do it now. -bool Plater::Enable(bool enable) -{ - bool retval = wxPanel::Enable(enable); - if (enable && retval) - this->restore_keyboard_focus(); - return retval; -} -void Plater::restore_keyboard_focus() -{ - // Activating the main frame, and no window has keyboard focus. - // Set the keyboard focus to the visible Canvas3D. - if (this->p->view3D->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) - this->p->view3D->get_wxglcanvas()->SetFocus(); - else if (this->p->preview->IsShown() && wxWindow::FindFocus() != this->p->view3D->get_wxglcanvas()) - this->p->preview->get_wxglcanvas()->SetFocus(); -} -#endif // Linux or Windows - // Get vector of extruder colors considering filament color, if extruder color is undefined. std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 6d742a832..d3eead4ed 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -149,16 +149,6 @@ public: void render_project_state_debug_window() const; #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW -#if defined(__linux__) || defined(_WIN32) - // wxWidgets callback to enable / disable window and all its children windows. - // called by wxProgressDialog when entering / leaving modal dialog loop. - // Unfortunately the wxProgressDialog calls Enable(true) after the wxEVT_ACTIVATE event is processed - // while MainFrame is not yet enabled, thus restoring focus in OnActivate() handler fails - // and we need to do it now. - bool Enable(bool enable) override; - void restore_keyboard_focus(); -#endif - Sidebar& sidebar(); const Model& model() const; Model& model(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 40ae0cb8c..5bc7c981e 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -9,7 +9,6 @@ #include "Notebook.hpp" #include "ButtonsDescription.hpp" #include "OG_CustomCtrl.hpp" -#include <initializer_list> namespace Slic3r { @@ -466,7 +465,7 @@ void PreferencesDialog::build(size_t selected_tab) #ifdef _WIN32 // Add "Dark Mode" tab - if (is_editor) { + { // 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) { @@ -519,21 +518,29 @@ void PreferencesDialog::build(size_t selected_tab) this->CenterOnParent(); } -void PreferencesDialog::update_ctrls_alignment() +std::vector<ConfigOptionsGroup*> PreferencesDialog::optgroups() { - int max_ctrl_width{ 0 }; - std::initializer_list<ConfigOptionsGroup*> og_list = { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() + std::vector<ConfigOptionsGroup*> out; + out.reserve(4); + for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() #ifdef _WIN32 , m_optgroup_dark_mode.get() #endif // _WIN32 - }; - for (auto og : og_list) { + }) + if (opt) + out.emplace_back(opt); + return out; +} + +void PreferencesDialog::update_ctrls_alignment() +{ + int max_ctrl_width{ 0 }; + for (ConfigOptionsGroup* og : this->optgroups()) if (int max = og->custom_ctrl->get_max_win_width(); max_ctrl_width < max) max_ctrl_width = max; - } if (max_ctrl_width) - for (auto og : og_list) + for (ConfigOptionsGroup* og : this->optgroups()) og->custom_ctrl->set_max_win_width(max_ctrl_width); } @@ -622,9 +629,8 @@ void PreferencesDialog::accept(wxEvent&) void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_optgroup_general->msw_rescale(); - m_optgroup_camera->msw_rescale(); - m_optgroup_gui->msw_rescale(); + for (ConfigOptionsGroup* og : this->optgroups()) + og->msw_rescale(); msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL }); @@ -788,7 +794,7 @@ void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key) }); std::pair<OG_CustomCtrl*, bool*> ctrl = { nullptr, nullptr }; - for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui }) { + for (ConfigOptionsGroup* opt_group : this->optgroups()) { ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1); if (ctrl.first && ctrl.second) { m_highlighter.init(ctrl); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 210b04d4f..6c8012195 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -6,6 +6,7 @@ #include <wx/dialog.h> #include <wx/timer.h> +#include <vector> #include <map> class wxColourPickerCtrl; @@ -61,6 +62,7 @@ protected: void create_settings_mode_widget(); void create_settings_text_color_widget(); void init_highlighter(const t_config_option_key& opt_key); + std::vector<ConfigOptionsGroup*> optgroups(); struct PreferencesHighlighter { diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 2cea580be..09ba48b58 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -98,8 +98,9 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo } if (post_actions.has(PrintHostPostUploadAction::StartSimulation)) { - auto* btn_print = add_button(wxID_YES, false, _L("Upload and Simulate")); - btn_print->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { + // Using wxID_MORE as a button identifier to be different from the other buttons, wxID_MORE has no other meaning here. + auto* btn_simulate = add_button(wxID_MORE, false, _L("Upload and Simulate")); + btn_simulate->Bind(wxEVT_BUTTON, [this, validate_path](wxCommandEvent&) { if (validate_path(txt_filename->GetValue())) { post_upload_action = PrintHostPostUploadAction::StartSimulation; EndDialog(wxID_OK); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 41794d294..335dd6619 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -885,6 +885,10 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) } m_postpone_update_ui = false; + + // When all values are rolled, then we hane to update whole tab in respect to the reverted values + update(); + update_changed_ui(); } @@ -1148,6 +1152,13 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "extruders_count") wxGetApp().plater()->on_extruders_change(boost::any_cast<size_t>(value)); + if (m_postpone_update_ui) { + // It means that not all values are rolled to the system/last saved values jet. + // And call of the update() can causes a redundant check of the config values, + // see https://github.com/prusa3d/PrusaSlicer/issues/7146 + return; + } + update(); } @@ -4481,7 +4492,7 @@ ConfigManipulation Tab::get_config_manipulation() return on_value_change(opt_key, value); }; - return ConfigManipulation(load_config, cb_toggle_field, cb_value_change); + return ConfigManipulation(load_config, cb_toggle_field, cb_value_change, nullptr, this); } diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 0ebe47a07..3602d6972 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -112,7 +112,17 @@ SCENARIO("2D convex hull of sinking object", "[3mf]") { { -91501496, 4243 } }; - bool res = hull_2d.points == result; + // Allow 1um error due to floating point rounding. + bool res = hull_2d.points.size() == result.size(); + if (res) + for (size_t i = 0; i < result.size(); ++ i) { + const Point &p1 = result[i]; + const Point &p2 = hull_2d.points[i]; + if (std::abs(p1.x() - p2.x()) > 1 || std::abs(p1.y() - p2.y()) > 1) { + res = false; + break; + } + } THEN("2D convex hull should match with reference") { REQUIRE(res); |