diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index f2d2f842e9..f959a6d343 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -151,7 +151,7 @@ jobs: if-no-files-found: error - name: Deploy Flatpak to nightly release - if: ${{github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0'}} + if: ${{github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3'}} uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index 6599f4e4de..4ee0e7f0e7 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -122,7 +122,7 @@ jobs: # Thanks to RaySajuuk, it's working now - name: Sign app and notary - if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14' + if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14' working-directory: ${{ github.workspace }} env: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} @@ -175,7 +175,7 @@ jobs: fi - name: Create DMG without notary - if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14' && github.ref != 'refs/heads/2.2.0' + if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14' && github.ref != 'refs/heads/2.2.3' working-directory: ${{ github.workspace }} run: | mkdir -p ${{ github.workspace }}/build/universal/Snapmaker_Orca_dmg @@ -217,7 +217,7 @@ jobs: if-no-files-found: warn - name: Deploy Mac release - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14' uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} @@ -228,7 +228,7 @@ jobs: max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted - name: Check if profile validator DMG exists - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14' id: check_dmg working-directory: ${{ github.workspace }} run: | @@ -242,7 +242,7 @@ jobs: shell: bash - name: Deploy Mac Snapmaker_Orca_profile_validator DMG release - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14' && steps.check_dmg.outputs.exists == 'true' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14' && steps.check_dmg.outputs.exists == 'true' uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} @@ -318,7 +318,7 @@ jobs: path: ${{ github.workspace }}/build/src/Release/Snapmaker_Orca_profile_validator.exe - name: Deploy Windows release portable - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest' uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} @@ -329,7 +329,7 @@ jobs: max_releases: 1 - name: Deploy Windows release installer - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest' uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} @@ -340,7 +340,7 @@ jobs: max_releases: 1 - name: Deploy Windows Snapmaker_Orca_profile_validator release - if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest' + if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest' uses: WebFreak001/deploy-nightly@v3.2.0 with: upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label} @@ -414,7 +414,7 @@ jobs: path: './build/src/Release/Snapmaker_Orca_profile_validator' - name: Deploy Ubuntu release - if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }} + if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }} env: ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }} uses: WebFreak001/deploy-nightly@v3.2.0 @@ -426,7 +426,7 @@ jobs: asset_content_type: application/octet-stream max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted - name: Deploy Ubuntu release - if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'ubuntu-24.04' }} + if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'ubuntu-24.04' }} uses: rickstaa/action-create-tag@v1 with: tag: "nightly-builds" @@ -435,7 +435,7 @@ jobs: message: "nightly-builds" - name: Deploy Ubuntu Snapmaker_Orca_profile_validator release - if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }} + if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }} env: ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }} uses: WebFreak001/deploy-nightly@v3.2.0 diff --git a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po index 9c47f3d18a..851261cf3e 100644 --- a/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po +++ b/localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po @@ -15259,7 +15259,7 @@ msgid "Point and point assembly" msgstr "点和点组装" msgid "Please do not include the special characters #, *, ;, \\, /, :, \", <, >, or | in filenames." -msgstr "在文件名中勿使用特殊字符"#"*""";""\\""/" ":"\""<"">"或"|"。" +msgstr "在文件名中勿使用特殊字符#, *, ;, \\, /, :, \", <, >或|。" msgid "The filename '%s' contains special characters (#, *, ;, \\, /, :, \", <, >, or |) which may cause issues.Do you wish to continue?" msgstr "文件名'%s'包含特殊字符(# * ; \\ / : \" < > |)可能会导致问题。是否继续?" diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index e470598989..070399c9ce 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -194,6 +194,7 @@ "0" ], "ramming_pressure_advance_value": "0.02", + "default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 nozzle)", "tool_change_temprature_wait": "0", "printer_notes": "", "machine_pause_gcode": "M600", diff --git a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json index 4a632b9ccc..2b13f97d41 100644 --- a/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.12 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -34,5 +34,6 @@ "support_threshold_angle": "25", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "gap_fill_target": "topbottom" } \ No newline at end of file diff --git a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json b/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json deleted file mode 100644 index 8f8e351318..0000000000 --- a/resources/profiles/Snapmaker/process/0.14 Standard @Snapmaker U1 (0.2 nozzle).json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "process", - "name": "0.14 Standard @Snapmaker U1 (0.2 nozzle)", - "inherits": "fdm_process_U1_0.14_nozzle_0.2", - "from": "system", - "setting_id": "GP027", - "instantiation": "true", - "description": "Compared with the default profile of a 0.2 mm nozzle, it has a bigger layer height, and results in slightly visible layer lines, but shorter printing time.", - "elefant_foot_compensation": "0.15", - "smooth_coefficient": "150", - "overhang_totally_speed": "50", - "compatible_printers": [ - "Snapmaker U1 (0.2 nozzle)" - ], - "enable_arc_fitting": "0", - "ooze_prevention": "1", - "precise_outer_wall": "0", - "prime_tower_brim_width": "5", - "prime_tower_width": "30", - "prime_volume": "32", - "slowdown_for_curled_perimeters": "0", - "standby_temperature_delta": "-150", - "support_type": "tree(auto)", - "wipe_tower_extra_rib_length": "8", - "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib" -} \ No newline at end of file diff --git a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json index 4e6124e8e7..ea287484af 100644 --- a/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/process/0.16 High Quality @Snapmaker U1 (0.4 nozzle).json @@ -34,5 +34,6 @@ "support_threshold_angle": "30", "support_type": "tree(auto)", "wipe_tower_extra_rib_length": "8", - "wipe_tower_wall_type": "rib" + "wipe_tower_wall_type": "rib", + "gap_fill_target": "topbottom" } \ No newline at end of file diff --git a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json b/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json deleted file mode 100644 index a7e1357013..0000000000 --- a/resources/profiles/Snapmaker/process/0.18 Standard @Snapmaker U1 (0.6 nozzle).json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "type": "process", - "name": "0.18 Standard @Snapmaker U1 (0.6 nozzle)", - "inherits": "fdm_process_U1_0.18_nozzle_0.6", - "from": "system", - "setting_id": "GP028", - "instantiation": "true", - "description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.", - "elefant_foot_compensation": "0.15", - "smooth_coefficient": "150", - "overhang_totally_speed": "50", - "compatible_printers": [ - "Snapmaker U1 (0.6 nozzle)" - ], - "filter_out_gap_fill": "1", - "gap_fill_target": "topbottom", - "internal_bridge_speed": "100%", - "layer_height": "0.25", - "prime_tower_brim_width": "5", - "prime_tower_width": "30", - "prime_volume": "41", - "seam_gap": "15%", - "wipe_tower_extra_rib_length": "8", - "wipe_tower_extra_spacing": "120%", - "wipe_tower_wall_type": "rib", - "enable_arc_fitting": "0", - "ooze_prevention": "1", - "precise_outer_wall": "0", - "slowdown_for_curled_perimeters": "0", - "standby_temperature_delta": "-150", - "support_type": "tree(auto)" -} \ No newline at end of file diff --git a/resources/web/flutter_web/version.changelog b/resources/web/flutter_web/version.changelog index 099ff73de9..425aa19eeb 100644 --- a/resources/web/flutter_web/version.changelog +++ b/resources/web/flutter_web/version.changelog @@ -6,4 +6,4 @@ - Feature: Added a new model entry ### Bugfix - Fixed the issue where devices would automatically disconnect when connected over Lan network \ No newline at end of file + Fixed the issue where devices would automatically disconnect when connected over Lan network diff --git a/scripts/flatpak/io.github.Snapmaker.Snapmaker_Orca.metainfo.xml b/scripts/flatpak/io.github.Snapmaker.Snapmaker_Orca.metainfo.xml index 2c6cda2ee4..452d3dc67b 100644 --- a/scripts/flatpak/io.github.Snapmaker.Snapmaker_Orca.metainfo.xml +++ b/scripts/flatpak/io.github.Snapmaker.Snapmaker_Orca.metainfo.xml @@ -38,7 +38,7 @@ #009688 - +

Version 2.2.4 release with improvements and bug fixes.

diff --git a/src/bury_cfg/bury_point.cpp b/src/bury_cfg/bury_point.cpp index 8c16dc5a0b..d369cbfade 100644 --- a/src/bury_cfg/bury_point.cpp +++ b/src/bury_cfg/bury_point.cpp @@ -5,6 +5,13 @@ #include static std::atomic isAgreeSlice(true); + +static std::atomic g_sentry_initialized(false); + +bool get_sentry_flags() { return g_sentry_initialized; } + +void set_sentry_flags(bool flags) { g_sentry_initialized = flags; } + bool get_privacy_policy() { return isAgreeSlice; diff --git a/src/bury_cfg/bury_point.hpp b/src/bury_cfg/bury_point.hpp index ebb8d4fa75..7d8a98b7fe 100644 --- a/src/bury_cfg/bury_point.hpp +++ b/src/bury_cfg/bury_point.hpp @@ -63,6 +63,9 @@ //webview bury point + BURY_API bool get_sentry_flags(); + BURY_API void set_sentry_flags(bool flags); + BURY_API bool get_privacy_policy(); BURY_API void set_privacy_policy(bool isAgree); BURY_API std::string get_timestamp_seconds(); diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 86fc705619..d634cf61ac 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -2094,25 +2094,38 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer() // brim with chamfer (gradual layer-by-layer reduction) int loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing; - // Apply chamfer reduction if feature is enabled and brim width is configured - if (m_wipe_tower_brim_width > 0 && m_prime_tower_brim_chamfer) { - if (!first_layer) { + // Apply brim logic based on chamfer setting + if (m_wipe_tower_brim_width > 0) { + if (first_layer) { + // First layer: always print full brim (loops_num unchanged) + } else if (m_prime_tower_brim_chamfer) { + // Non-first layer + chamfer enabled: apply gradual reduction // Calculate distance from first layer with tool changes size_t current_idx = m_layer_info - m_plan.begin(); int dist_to_1st = (int)current_idx - (int)m_first_layer_idx; - // Stop print chamfer if depth changes - bool depth_changed = (m_layer_info->depth != m_plan[m_first_layer_idx].depth); - if (depth_changed) { + // Validate m_first_layer_idx to prevent invalid index access + if (m_first_layer_idx == size_t(-1) || m_first_layer_idx >= m_plan.size()) { + // Invalid first layer index, don't print brim loops_num = 0; } else { - // Limit max chamfer width to configured value - int chamfer_loops_num = (int)(m_prime_tower_brim_chamfer_max_width / spacing); - loops_num = std::min(loops_num, chamfer_loops_num) - dist_to_1st; - // Ensure loops_num doesn't go negative - if (loops_num < 0) loops_num = 0; + // Stop print chamfer if depth changes + bool depth_changed = (m_layer_info->depth != m_plan[m_first_layer_idx].depth); + if (depth_changed) { + loops_num = 0; + } else { + // Limit max chamfer width to configured value + int chamfer_loops_num = (int) (m_prime_tower_brim_chamfer_max_width / spacing); + loops_num = std::min(loops_num, chamfer_loops_num) - dist_to_1st; + // Ensure loops_num doesn't go negative + if (loops_num < 0) + loops_num = 0; + } } + } else { + // Non-first layer + chamfer disabled: don't print brim (revert to original behavior) + loops_num = 0; } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 116dfb1ac5..4bcb6be574 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5747,7 +5747,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("Enable gradual layer-by-layer reduction of the brim around the prime tower. " "This creates a chamfered/tapered effect, reducing material usage while " "maintaining first layer adhesion."); - def->mode = comAdvanced; + def->mode = comDevelop; def->set_default_value(new ConfigOptionBool(true)); def = this->add("prime_tower_brim_chamfer_max_width", coFloat); @@ -5756,7 +5756,7 @@ void PrintConfigDef::init_fff_params() "The brim will reduce within this distance. Larger values create a more " "gradual taper but take more layers to complete."); def->sidetext = "mm"; // milimeters, don't need translation - def->mode = comAdvanced; + def->mode = comDevelop; def->min = 0.; def->set_default_value(new ConfigOptionFloat(4.0)); diff --git a/src/sentry_wrapper/SentryWrapper.cpp b/src/sentry_wrapper/SentryWrapper.cpp index 380220196b..3299b6ae4d 100644 --- a/src/sentry_wrapper/SentryWrapper.cpp +++ b/src/sentry_wrapper/SentryWrapper.cpp @@ -50,6 +50,23 @@ namespace Slic3r { #define SENTRY_KEY_LEVEL "level" +#ifdef _WIN32 +// C-style wrapper function for sentry_init to allow use of __try/__except +// This function must be C-style because __try/__except cannot be used in functions +// that require C++ object unwinding (functions with C++ objects that need destructors) +extern "C" { + static int safe_sentry_init(sentry_options_t* options) { + int result = -1; + __try { + result = sentry_init(options); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // Exception occurred during sentry_init + result = -1; + } + return result; + } +} +#endif static sentry_value_t on_crash_callback(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure) { @@ -252,31 +269,66 @@ void initSentryEx() sentry_options_set_logs_with_attributes(options, true); // Set release version for symbolication - // This must match the release used when uploading symbols sentry_options_set_release(options, Snapmaker_VERSION); + bool init_success = false; - sentry_init(options); - sentry_start_session(); +#ifdef _WIN32 + // Use C-style wrapper function to safely call sentry_init with SEH exception handling + int result = safe_sentry_init(options); + std::cout << "sentry_init returned: " << result << std::endl; + if (result == 0) { + init_success = true; + std::cout << "sentry_init succeeded, init_success set to true" << std::endl; + } else { + std::cout << "Error: sentry_init failed or exception occurred, Sentry initialization failed (result=" << result << ")" << std::endl; + // Exception occurred or sentry_init returned error, sentry_init was not successfully called + // so we need to free options + sentry_options_free(options); + set_sentry_flags(false); + return; // Exit early if initialization fails + } +#else + int result = sentry_init(options); + std::cout << "sentry_init returned: " << result << std::endl; + if (result == 0) { + init_success = true; + std::cout << "sentry_init succeeded, init_success set to true" << std::endl; + } else { + std::cout << "Warning: sentry_init returned non-zero: " << result << std::endl; + } +#endif - sentry_set_tag("snapmaker_version", Snapmaker_VERSION); + // Start session and set tags only if initialization succeeded + if (init_success) { + std::cout << "Starting Sentry session and setting flags..." << std::endl; + sentry_start_session(); + set_sentry_flags(true); + + sentry_set_tag("snapmaker_version", Snapmaker_VERSION); - std::string flutterVersion = common::get_flutter_version(); - if (!flutterVersion.empty()) - sentry_set_tag("flutter_version", flutterVersion.c_str()); + std::string flutterVersion = common::get_flutter_version(); + if (!flutterVersion.empty()) + sentry_set_tag("flutter_version", flutterVersion.c_str()); - std::string machineID = common::getMachineId(); - if (!machineID.empty()) - sentry_set_tag("machine_id", machineID.c_str()); + std::string machineID = common::getMachineId(); + if (!machineID.empty()) + sentry_set_tag("machine_id", machineID.c_str()); - std::string pcName = common::get_pc_name(); - if (!pcName.empty()) - sentry_set_tag("pc_name", pcName.c_str()); + std::string pcName = common::get_pc_name(); + if (!pcName.empty()) + sentry_set_tag("pc_name", pcName.c_str()); + } else { + set_sentry_flags(false); + } } } void exitSentryEx() -{ - sentry_close(); +{ + if (get_sentry_flags()) { + sentry_close(); + set_sentry_flags(false); + } } void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel, const std::string& logContent, @@ -285,6 +337,11 @@ void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel, const std::string& logTagValue, const std::string& logTraceId) { + // Check if Sentry is initialized before using it + if (!get_sentry_flags()) { + return; + } + if (!get_privacy_policy()) { return; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9a67b0bcce..86c4c78975 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1331,10 +1331,16 @@ void GLCanvas3D::reset_volumes() m_dirty = true; auto pLater = wxGetApp().plater(); - - if (pLater && wxGetApp().plater()->get_notification_manager()) - { - _set_warning_notification(EWarning::ObjectOutside, false); + if (pLater) { + auto* notification_mgr = pLater->get_notification_manager(); + if (notification_mgr) { + // Only update notification if we can safely access Plater's current canvas + // This ensures Plater::priv structure is still valid + auto* canvas = pLater->get_current_canvas3D(); + if (canvas) { + _set_warning_notification(EWarning::ObjectOutside, false); + } + } } } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 51fed40c67..ce41b42ac1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3660,6 +3660,8 @@ if (res) { load_current_presets(); update_publish_status(); mainframe->refresh_plugin_tips(); + + m_fltviews.reload_all(); // BBS: remove SLA related message } } catch (std::exception &) { @@ -5976,7 +5978,9 @@ void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_ // so we put it into an inner scope PreferencesDialog dlg(mainframe, open_on_tab, highlight_option); dlg.ShowModal(); - this->plater_->get_current_canvas3D()->force_set_focus(); + GLCanvas3D* canvas = this->plater_->get_current_canvas3D(); + if (canvas) + canvas->force_set_focus(); // BBS //app_layout_changed = dlg.settings_layout_changed(); #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER @@ -6727,6 +6731,7 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage wxGetApp().sidebar().update_nozzle_settings(); update_publish_status(); mainframe->refresh_plugin_tips(); + m_fltviews.reload_all(); // BBS: remove SLA related message } auto isAgree = wxGetApp().app_config->get("app", PRIVACY_POLICY_FLAGS); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 7221430fbd..d02478b710 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -903,7 +903,7 @@ public: } } - void relead_all() { + void reload_all() { for (const auto& view : webviews) { auto ptr = view.first; wxString new_url = app->get_international_url(view.second); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index d9d4c8143c..43fac34f94 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -330,11 +330,25 @@ bool CheckboxFileDialog::get_checkbox_value() const } +// GTK requires width >= -1 and height > 0 for gtk_window_resize/set_size_request. +// Use minimum 100 to avoid 0 or negative dimensions from bad config or Intersect(). +static const int WINDOW_MIN_WIDTH = 100; +static const int WINDOW_MIN_HEIGHT = 100; + +static void clamp_rect_to_minimum_size(wxRect &rect) +{ + if (rect.width < WINDOW_MIN_WIDTH) + rect.width = WINDOW_MIN_WIDTH; + if (rect.height < WINDOW_MIN_HEIGHT) + rect.height = WINDOW_MIN_HEIGHT; +} + WindowMetrics WindowMetrics::from_window(wxTopLevelWindow *window) { WindowMetrics res; res.rect = window->GetScreenRect(); res.maximized = window->IsMaximized(); + clamp_rect_to_minimum_size(res.rect); return res; } @@ -362,6 +376,7 @@ boost::optional WindowMetrics::deserialize(const std::string &str WindowMetrics res; res.rect = wxRect(metrics[0], metrics[1], metrics[2], metrics[3]); + clamp_rect_to_minimum_size(res.rect); res.maximized = metrics[4] != 0; return res; @@ -370,6 +385,11 @@ boost::optional WindowMetrics::deserialize(const std::string &str void WindowMetrics::sanitize_for_display(const wxRect &screen_rect) { rect = rect.Intersect(screen_rect); + // Intersect can yield 0 or negative width/height; GTK asserts on that. + clamp_rect_to_minimum_size(rect); + // Keep within display (e.g. after monitor change) + rect.x = std::max(screen_rect.x, std::min(rect.x, screen_rect.x + screen_rect.width - rect.width)); + rect.y = std::max(screen_rect.y, std::min(rect.y, screen_rect.y + screen_rect.height - rect.height)); // Prevent the window from going too far towards the right and/or bottom edge // It's hardcoded here that the threshold is 80% of the screen size diff --git a/src/slic3r/GUI/HttpServer.hpp b/src/slic3r/GUI/HttpServer.hpp index 2c98d9ccb0..394f513803 100644 --- a/src/slic3r/GUI/HttpServer.hpp +++ b/src/slic3r/GUI/HttpServer.hpp @@ -16,7 +16,7 @@ #define LOCALHOST_PORT 13618 #define PAGE_HTTP_PORT 13619 -#define LOCALHOST_URL "http://localhost:" +#define LOCALHOST_URL "http://127.0.0.1:" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9915dd671c..48a4648e99 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -270,6 +270,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ // Load the icon either from the exe, or from the ico file. SetIcon(main_frame_icon(wxGetApp().get_app_mode())); +#ifdef __WXGTK__ + // GTK/X11: set minimum size before any layout so gtk_widget_set_size_request and + // gtk_window_resize never see 0 or negative dimensions (avoids assertion failures). + SetMinSize(wxGetApp().get_min_size()); +#endif + // initialize tabpanel and menubar init_tabpanel(); if (wxGetApp().is_gcode_viewer()) @@ -501,8 +507,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ //FIXME maybe this is useful for __WXGTK3__ as well? #if __APPLE__ Bind(wxEVT_MOVE, [](wxMoveEvent& event) { - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D(); + if (canvas) { + canvas->set_as_dirty(); + canvas->request_extra_frame(); + } event.Skip(); }); #endif @@ -2240,7 +2249,9 @@ static wxMenu* generate_help_menu() append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the Day"), _L("Show Tip of the Day"), [](wxCommandEvent&) { wxGetApp().plater()->get_dailytips()->open(); - wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); + GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D(); + if (canvas) + canvas->set_as_dirty(); }); // Report a bug @@ -2515,8 +2526,11 @@ void MainFrame::init_menubar_as_editor() auto handle_key_event = [](wxKeyEvent& evt) { if (wxGetApp().imgui()->update_key_data(evt)) { - wxGetApp().plater()->get_current_canvas3D()->render(); - return true; + GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D(); + if (canvas) { + canvas->render(); + return true; + } } return false; }; diff --git a/src/slic3r/GUI/MarkdownTip.cpp b/src/slic3r/GUI/MarkdownTip.cpp index 0ceee2a278..17be15ba00 100644 --- a/src/slic3r/GUI/MarkdownTip.cpp +++ b/src/slic3r/GUI/MarkdownTip.cpp @@ -146,8 +146,15 @@ bool MarkdownTip::ShowTip(wxPoint pos, std::string const &tip, std::string const if (_tipView->GetParent() == this) { wxSize size = wxDisplay(this).GetClientArea().GetSize(); _requestPos = pos; + // Ensure display size is valid + if (size.y <= 0) size.y = 600; if (pos.y + this->GetSize().y > size.y) - pos.y = size.y - this->GetSize().y; + { + pos.y = 0; + if( (size.y - this->GetSize().y) > 0) + pos.y = (size.y - this->GetSize().y); + } + this->SetPosition(pos); if (tipChanged || _hide) { _hide = false; @@ -258,11 +265,20 @@ void MarkdownTip::OnTitleChanged(wxWebViewEvent& event) _lastHeight = height; height *= 1.25; height += 50; wxSize size = wxDisplay(this).GetClientArea().GetSize(); + // Ensure display size is valid + if (size.y <= 0) size.y = 600; if (height > size.y) height = size.y; wxPoint pos = _requestPos; - if (pos.y + height > size.y) - pos.y = size.y - height; + if (pos.y + height > size.y) + { + pos.y= 0; + if(size.y-height>0) + pos.y = size.y - height; + } + + // Ensure height is valid + if (height <= 0) height = 100; this->SetSize({ 400, (int)height }); this->SetPosition(pos); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 905e8582f4..20ae8863e7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1903,11 +1903,14 @@ void Sidebar::update_presets(Preset::Type preset_type) } Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - if (auto printer_structure_opt = printer_preset.config.option>("printer_structure")) { - wxGetApp().plater()->get_current_canvas3D()->get_arrange_settings().align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3); + GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D(); + if (canvas) { + if (auto printer_structure_opt = printer_preset.config.option>("printer_structure")) { + canvas->get_arrange_settings().align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3); + } + else + canvas->get_arrange_settings().align_to_y_axis = false; } - else - wxGetApp().plater()->get_current_canvas3D()->get_arrange_settings().align_to_y_axis = false; break; } @@ -2749,9 +2752,12 @@ void Sidebar::update_ui_from_settings() // BBS //p->object_manipulation->update_ui_from_settings(); // update Cut gizmo, if it's open - p->plater->canvas3D()->update_gizmos_on_off_state(); - p->plater->set_current_canvas_as_dirty(); - p->plater->get_current_canvas3D()->request_extra_frame(); + GLCanvas3D* canvas = p->plater->canvas3D(); + if (canvas) { + canvas->update_gizmos_on_off_state(); + p->plater->set_current_canvas_as_dirty(); + p->plater->get_current_canvas3D()->request_extra_frame(); + } #if 0 p->object_list->apply_volumes_order(); #endif @@ -5671,7 +5677,13 @@ Selection& Plater::priv::get_selection() Selection& Plater::priv::get_curr_selection() { - return get_current_canvas3D()->get_selection(); + GLCanvas3D* canvas = get_current_canvas3D(); + if (!canvas) { + // During destruction, return a reference to a static empty selection + static Selection empty_selection; + return empty_selection; + } + return canvas->get_selection(); } int Plater::priv::get_selected_object_idx() const @@ -5701,10 +5713,15 @@ void Plater::priv::selection_changed() } // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) - if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { - assemble_view->render(); - } else { - view3D->render(); + GLCanvas3D* canvas = get_current_canvas3D(); + if (canvas) { + if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + if (assemble_view) + assemble_view->render(); + } else { + if (view3D) + view3D->render(); + } } } @@ -6473,7 +6490,10 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const return false; } - wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas()); + GLCanvas3D* canvas = q->get_current_canvas3D(); + if (!canvas) + return false; + wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), canvas->get_wxglcanvas()); if (!snapshot.empty()) q->take_snapshot(snapshot); @@ -6770,7 +6790,13 @@ void Plater::priv::reload_from_disk() if (color_dlg.ShowModal() != wxID_OK) { filament_ids.clear(); } }; wxBusyCursor wait; - wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas()); + GLCanvas3D* canvas = q->get_current_canvas3D(); + if (!canvas) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "canvas is nullptr"; + continue; + } + wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), canvas->get_wxglcanvas()); Model new_model; try @@ -8839,16 +8865,26 @@ void Plater::priv::set_current_canvas_as_dirty() GLCanvas3D* Plater::priv::get_current_canvas3D(bool exclude_preview) { - if (current_panel == view3D) + // During destruction, these pointers may be null or point to destroyed objects + // Add null checks to prevent crashes during shutdown + if (current_panel == view3D) { + if (view3D) + return view3D->get_canvas3d(); + } + else if (!exclude_preview && (current_panel == preview)) { + if (preview) + return preview->get_canvas3d(); + } + else if (current_panel == assemble_view) { + if (assemble_view) + return assemble_view->get_canvas3d(); + } + + //BBS default set to view3D, but check if it's still valid + if (view3D) return view3D->get_canvas3d(); - else if (!exclude_preview && (current_panel == preview)) - return preview->get_canvas3d(); - else if (current_panel == assemble_view) - return assemble_view->get_canvas3d(); - else //BBS default set to view3D - return view3D->get_canvas3d(); - - //return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr); + + return nullptr; } void Plater::priv::unbind_canvas_event_handlers() @@ -9386,7 +9422,13 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle"); } } - const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager(); + GLCanvas3D* canvas = get_current_canvas3D(); + if (!canvas) { + // During destruction, skip snapshot + return; + } + + const GLGizmosManager& gizmos = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager(); if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator) this->undo_redo_stack().clear(); @@ -9420,7 +9462,9 @@ void Plater::priv::undo() // BBS: undo-redo until modify record while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); if (it_current == snapshots.begin()) return; - if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + GLCanvas3D* canvas = get_current_canvas3D(); + if (!canvas) return; + if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction && it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo && it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoNoAction && @@ -9528,9 +9572,13 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator // Make a copy of the snapshot, undo/redo could invalidate the iterator const UndoRedo::Snapshot snapshot_copy = *it_snapshot; // Do the jump in time. + GLCanvas3D* canvas = get_current_canvas3D(); + if (!canvas) return; + + bool is_assemble = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView; if (it_snapshot->timestamp < this->undo_redo_stack().active_snapshot_time() ? - this->undo_redo_stack().undo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) : - this->undo_redo_stack().redo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) { + this->undo_redo_stack().undo(model, is_assemble ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), is_assemble ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) : + this->undo_redo_stack().redo(model, is_assemble ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) { if (printer_technology_changed) { // Switch to the other printer technology. Switch to the last printer active for that particular technology. AppConfig *app_config = wxGetApp().app_config; @@ -9608,7 +9656,11 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */) { - get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear(); + GLCanvas3D* canvas = get_current_canvas3D(); + if (!canvas) return; + + bool is_assemble = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView; + is_assemble ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear(); // Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies). this->update((unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE | (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE); // Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot. @@ -9617,10 +9669,10 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo // triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size. this->undo_redo_stack().release_least_recently_used(); //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) - get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + is_assemble ? assemble_view->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances) : this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances); - get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? + is_assemble ? assemble_view->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot) : this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot); @@ -11299,7 +11351,9 @@ void Plater::load_gcode(const wxString& filename) p->preview->reload_print(false, m_only_gcode); wxGetApp().mainframe->select_tab(MainFrame::tpPreview); p->set_current_panel(p->preview, true); - p->get_current_canvas3D()->render(); + GLCanvas3D* canvas = p->get_current_canvas3D(); + if (canvas) + canvas->render(); //p->notification_manager->bbl_show_plateinfo_notification(into_u8(_L("Preview only mode for gcode file."))); wxBusyCursor wait; @@ -12299,7 +12353,9 @@ void Plater::set_selected_visible(bool visible) Plater::TakeSnapshot snapshot(this, "Set Selected Objects Visible in AssembleView"); get_ui_job_worker().cancel_all(); - p->get_current_canvas3D()->set_selected_visible(visible); + GLCanvas3D* canvas = p->get_current_canvas3D(); + if (canvas) + canvas->set_selected_visible(visible); } @@ -12319,7 +12375,9 @@ void Plater::remove_selected() //BBS delete current selected // p->view3D->delete_selected(); - p->get_current_canvas3D()->delete_selected(); + GLCanvas3D* canvas = p->get_current_canvas3D(); + if (canvas) + canvas->delete_selected(); } void Plater::increase_instances(size_t num) @@ -14592,7 +14650,9 @@ void Plater::changed_object(ModelObject &object){ p->schedule_background_process(); // Check outside bed - get_current_canvas3D()->requires_check_outside_state(); + GLCanvas3D* canvas = get_current_canvas3D(); + if (canvas) + canvas->requires_check_outside_state(); } void Plater::changed_object(int obj_idx) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 18d7528336..d7682b8451 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -205,6 +205,9 @@ void PresetComboBox::update_selection() // A workaround for a set of issues related to text fitting into gtk widgets: #if defined(__WXGTK20__) || defined(__WXGTK3__) + // Guard: m_widget may not be a GtkCellLayout (e.g. not yet realized, or GtkComboBoxText in Flatpak/GTK3) + if (!m_widget || !GTK_IS_CELL_LAYOUT(m_widget)) + return; GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_widget)); // 'cells' contains the GtkCellRendererPixBuf for the icon, diff --git a/src/slic3r/GUI/SSWCP.cpp b/src/slic3r/GUI/SSWCP.cpp index 1dbdff0177..d459f4504c 100644 --- a/src/slic3r/GUI/SSWCP.cpp +++ b/src/slic3r/GUI/SSWCP.cpp @@ -1523,7 +1523,11 @@ void SSWCP_Instance::update_filament_info(const json& objects, bool send_message // 名称特殊处理 if (type == "TPU") { - name = vendor + " " + type; + if (sub_type == "95A HF") { + name = vendor + " " + type + ((sub_type != "NONE" && sub_type != "") ? " " + sub_type : ""); + } else { + name = vendor + " " + type; + } } else if (sub_type == "Support") { name = vendor + " Support" + " For " + type; } else { diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index 5c405af516..9a67906dba 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -496,7 +496,7 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt) } } - wxGetApp().fltviews().relead_all(); + wxGetApp().fltviews().reload_all(); } this->EndModal(wxID_OK); diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index ab48a27d2d..6ec0265564 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -368,7 +368,12 @@ void DropDown::messureSize() wxWindow::SetSize(szContent); #ifdef __WXGTK__ // Gtk has a wrapper window for popup widget - gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y); + // Fix for GNOME Platform 48 X11 backend: ensure size is valid before calling gtk_window_resize + int gtk_width = szContent.x; + int gtk_height = szContent.y; + if (gtk_width <= 0) gtk_width = 100; + if (gtk_height <= 0) gtk_height = 100; + gtk_window_resize(GTK_WINDOW(m_widget), gtk_width, gtk_height); #endif need_sync = false; } @@ -384,6 +389,10 @@ void DropDown::autoPosition() size = rowSize; size.y *= std::min((size_t)15, texts.size()); size.y += texts.size() > 15 ? rowSize.y / 2 : 0; +#ifdef __WXGTK__ + if (size.x < 1) size.x = 1; + if (size.y < 1) size.y = 1; +#endif if (size != GetSize()) { wxWindow::SetSize(size); offset = wxPoint(); @@ -396,6 +405,10 @@ void DropDown::autoPosition() if (GetPosition().y + size.y + 10 > drect.GetBottom()) { if (use_content_width && texts.size() <= 15) size.x += 6; size.y = drect.GetBottom() - GetPosition().y - 10; +#ifdef __WXGTK__ + if (size.y < 1) size.y = 1; + if (size.x < 1) size.x = 1; +#endif wxWindow::SetSize(size); if (selection >= 0) { if (offset.y + rowSize.y * (selection + 1) > size.y) diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index e3cf9b4531..24507cb776 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -30,7 +30,7 @@ class DropDown : public PopupWindow wxSize textSize; wxSize iconSize; - wxSize rowSize; + wxSize rowSize{100, 30}; // Initialize with default values StateHandler state_handler; StateColor text_color; diff --git a/src/slic3r/GUI/Widgets/WebView.cpp b/src/slic3r/GUI/Widgets/WebView.cpp index c388f39ed4..d3633ee40c 100644 --- a/src/slic3r/GUI/Widgets/WebView.cpp +++ b/src/slic3r/GUI/Widgets/WebView.cpp @@ -17,6 +17,11 @@ #endif #include "sentry_wrapper/SentryWrapper.hpp" + +#if defined(__linux__) +#include +#endif + #ifdef __WIN32__ #include #include @@ -275,10 +280,18 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) // And the memory: file system webView->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); #else - // With WKWebView handlers need to be registered before creation + // Handlers must be registered before Create(). Linux (WebKit2): scheme is process-global, register once to avoid "Cannot register URI scheme ... more than once". + // macOS (WKWebView): scheme is per-view, each WebView needs its own handlers. +#if defined(__linux__) + static std::once_flag s_wxfs_memory_handlers_once; + std::call_once(s_wxfs_memory_handlers_once, [webView]() { + webView->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); + webView->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); + }); +#else webView->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); - // And the memory: file system webView->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); +#endif webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); webView->SetUserAgent(wxString::Format("SM-Slicer/v%s (%s) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)", SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light"));