Merge build_platform_engineering into main - #949
Merged
Merged
Conversation
# Conflicts: # resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.2 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.4 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.6 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ABS @U1 0.8 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.2 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.4 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.6 nozzle.json # resources/profiles/Snapmaker/filament/Snapmaker ASA @U1 0.8 nozzle.json
Keep CPack NSIS extras ASCII so makensis ACP does not fail, restore cmake_install.cmake generation for deps_src, and keep Flatpak matrix running if one arch fails. Co-authored-by: Cursor <cursoragent@cursor.com>
…a, fix Quit-on-No
Replace the web logo assets under resources/web/image with the new Orca icon design (same artwork as the flutter_web 2.3.38 official-site logo master): - logo.png: 154x154, new full-bleed icon with the original rounded-corner silhouette preserved (wizard/homepage render the PNG directly, corners are baked in) - logo2.png: 339x406, square icon kept at original proportions and centered with equal black padding, no stretching The web bundle copies were missed by the app icon update in #858, which only touched resources/images/* and the app icon files.
Opening a model from Snapmaker Space downloads and loads it correctly, and then post_init() throws it away. The model appears on the plate for a moment, the plate clears and the title becomes "Untitled". macOS does not pass the URL in argv. LaunchServices starts the app first and delivers the URL afterwards through MacOpenURL, so init_params->input_files is empty and switch_to_3d is never set. post_init() therefore takes its ordinary startup path, sends the user to the home page and calls trigger_restore_project, which starts a blank project over the model MacOpenURL is loading. Windows is unaffected because the URL does arrive in argv, switch_to_3d is true, and that whole block is skipped. Set a flag in MacOpenURL and check it where switch_to_3d cannot reach, which gives the URL path on macOS the same treatment it already gets elsewhere. Fixes #851
#865) * fix(mac): stabilize local device WebView loads and printer page switching Declare localhost ATS/local-network entitlements, serialize WKWebView navigations to avoid cancelled -999 white screens, and reload the Device tab URL when switching between U1 and non-U1 printers. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mac): skip destroyed WKWebView script handler installs Guard CallAfter and delayed AddScriptMessageHandler against views already removed from g_webviews, matching BambuStudio OrcaSlicer#11004 / OrcaSlicer#10968. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: restore calibration ConfigOptionBools/Floats types in Plater A later WebView change accidentally reverted #861's arrayed option types. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mac): let Device WebView navigation gate receive LOADED/ERROR Bind PrinterWebView handlers on the panel and Skip so WKWebView settle can clear the in-flight URL. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Alves <alves@Alvesmac-3.local> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: alves <‘liulikeqian@gmail.com’>
Bugfix 2.3.7 alves merge with main
* Refresh MMU gizmo after filament color changes * Fix stale filament choices after filament removal * Fix object extruder remapping for physical-to-mixed merges * Refresh filament colors and sidebar after mixed filament removal
…tpServer (#870) * Fix crash on MQTT disconnect (MQTT/MoonRaker/HttpServer) * Fix remaining MQTT lifetime hazards and harden HTTP server shutdown MQTT: - Add MqttClient::create() factory that caches the client's own weak_ptr (self_) at construction; Paho callbacks use it instead of shared_from_this(), which threw bad_weak_ptr for raw new-ed clients and during destruction. A tearing_down_ flag guards late callbacks. - SSWCP sw_get_pin_code: keep the one-shot pin-code client in a static shared_ptr released on the UI thread only (was raw new/delete racing the Paho receive thread); parse the response with allow_exceptions= false so a malformed payload cannot terminate the process inside the C callback. - Moonraker_Mqtt: check callback-map emptiness under m_cbs_mtx in on_status_arrived/on_notification_arrived. HttpServer: - Create the IOServer on the calling thread; the io thread no longer writes server_, which raced the locked readers. - Guard start_health_check against assigning to our own joinable thread handle (std::terminate) when stop() disabled the check while the health thread was inside restart() -> start(). - Hold m_server_mtx across restart() teardown and start() so is_healthy() cannot observe the torn-down state and trigger a second restart. * fix: make start_http_server atomic and join health-check thread outside m_server_mtx Address review findings on PR #870: - start_http_server is written by the io thread's exception handler without holding m_server_mtx and read unlocked by is_started()/ set_port() and the health-check loop, so it must be atomic. - Split start() into start_locked() plus tail: restart() and start() now call start_health_check() only after m_server_mtx is released. A retired health-check thread can still be blocked in is_healthy() waiting for that lock; joining it via start_health_check() while holding the lock deadlocks (T1 holds m_server_mtx waiting on join, T2 waits on m_server_mtx to finish). * fix: guard time_sync_manager_ with a mutex and break engine self-capture in failure callback Address second-round review findings on PR #870: - Moonraker: time_sync_manager_ is reset by disconnect() on the calling thread while Paho receive-thread callbacks and async_* APIs on arbitrary threads read it. All reads now go through a locked time_sync_manager_snapshot(), the disconnect reset happens under the same mutex, and SetMessageCallback(nullptr) is moved BEFORE the reset so no new dispatch can start once the manager is dropped. - SSWCP: the connection failure callback captured the engine shared_ptr while being stored inside the engine itself, keeping its refcount >= 1 forever — ~MqttClient (the only place the callback is cleared) could never run. Capture a weak_ptr instead.
catch2/catch.hpp was removed in Catch2 v3, which made libslic3r_tests fail to compile with C1083 when BUILD_TESTS=ON. Include catch_test_macros.hpp instead, matching the other test files; the test only uses TEST_CASE/CHECK. Verified with cmake --build build --target libslic3r_tests --config Release and the full ctest baseline (503/508 passed; remaining failures are pre-existing).
initial_layer_speed is a coFloats vector, but the test accessed it as ConfigOptionFloat. option<T>() is type-safe and returns nullptr on mismatch, so the assignment dereferenced null and crashed the test with SIGSEGV before any section ran. Access the vector with the correct ConfigOptionFloats type and indexed placeholder syntax, and pin the engine's documented behavior that coFloatsOrPercents vectors are not addressable (Unsupported vector variable type). Verified: the scenario now passes all 70 assertions in Release and RelWithDebInfo; full libslic3r ctest is 309/310 with only the pre-existing SSWCP cached-slot failure remaining.
select_complete_cached_nozzle_info deliberately preserves the caller's flows when cached flow types are incomplete: the only production caller (Plater.cpp) passes freshly resolved nozzle volume types from objects.query, and an incomplete cache must not wipe that live data. The header contract documents this: flows are replaced only when every cached flow is valid. The test demanded flows.empty() in that case, contradicting the implementation and the caller. Expect the direct values to be preserved instead. Verified: [SSWCPProtocol] passes 77 assertions in 10 test cases; the full libslic3r module is 310/310 via ctest.
- Delete the per-user HKCU URL protocol keys the app writes at runtime, alongside the existing HKLM cleanup. - Move the MUI start-menu registry key deletion after the shortcut cleanup and complete the fallback pass, so a custom Start Menu folder is fully removed on uninstall (the stock template deleted the folder record before reading it and the fallback pass skipped the application shortcut).
level_str returns const char*, so == against a string literal compares pointers. The test only passed when the linker pooled the identical literals across translation units into one address; any code layout change can silently flip the result. Wrap the result in std::string_view so the comparison is by content and the test is robust against linker layout.
* fix(mac): regenerate app icons for consistent sizing across all surfaces Follow-up to the icon update in #858/#867. Three assets still carried inconsistent geometry after that change: - Icon.icns / Snapmaker_Orca.icns: the 16/32px frames were full-bleed while 64px+ frames carried a 0.89 tile ratio, and the @2x retina variants (ic11-ic14) present in the previous file were dropped, so the icon visibly changed size between Dock, Cmd-Tab and Finder contexts. The 1024px source frame was also off-center (81px top vs 33px bottom margin). Regenerated via iconutil from the 1024px master normalized to the Apple icon grid (824/1024 tile, centered), giving a uniform 0.805 ratio across all sizes with @2x variants restored. - Snapmaker_Orca-mac_256px.ico (macOS Dock taskbar icon, MainFrame): re-exported from the same normalized 256px frame so the custom Dock icon matches the bundle icon sizing. - splash_app_icon.svg: the splash glyph was an early thinner version of the new mark; retraced the glyph from the 1024px icon master (same black tile, tile color #171717 unchanged). (cherry picked from commit c76c3d5) * feat(mac): adopt Tahoe layered app icon with icns fallback macOS 26 renders legacy flat .icns icons in a shrunken legacy slot (~49px vs ~56px for native icons): Tahoe normalizes legacy tiles, so the app icon looked a size smaller in the Dock and no icns geometry could fix it. Ship a native layered icon (Icon Composer .icon format): - resources/AppIcon.icon: background fill #171717 (the system draws the standard squircle plate) + foreground white S glyph traced from splash_app_icon.svg at the same 0.356 glyph/tile ratio - resources/Assets.car: compiled via 'xcrun actool' (Xcode 26+; committed prebuilt so older build machines need no actool) - bundle Info.plist gains CFBundleIconName=AppIcon; the CFBundleIconFile icns stays as fallback for macOS 12-15 Note: on some older macOS releases the flattened new icon takes precedence over the icns; the artwork is identical, so acceptable. * fix(mac): show bundle icon in Dock while running The runtime Dock icon (NSApp.applicationIconImage via wxTaskBarIcon SetIcon) renders the raw image in the Dock tile, bypassing the macOS 26 native icon pipeline: the legacy Snapmaker_Orca-mac_256px.ico has its tile margins baked in, so the running icon showed ~8% smaller than neighboring tiles. Drop the runtime override and let the Dock use the bundle icon (layered .icon with icns fallback) at full native size. The right-click 'New Window' menu is unaffected - wxWidgets attaches it in the wxTaskBarIcon constructor, not in SetIcon. * chore: remove dead macOS Dock icon asset * build: add macOS icon regeneration script * test: guard icns structure and Assets.car * revert: restore original splash glyph geometry The retrace in a652f8f used the icon master's 0.356 glyph/tile ratio, but the original Figma splash export uses 0.393 - the splash mark shrank ~9% on all platforms and floated above center. Restore the original export; the .icon package keeps the master-ratio glyph for macOS 26. * build: add icns grid geometry self-check The regenerated icns drifted off the Apple 824/1024 grid (748px tile, ~22px above center) because the tile was drawn around the under-scaled traced glyph. Add a PIL-based self-check that fails regeneration on grid/size/centering drift, and sync the images/Snapmaker_Orca.icns twin. * fix(mac): restore icns to the 824/1024 icon grid The previous regeneration drifted off the Apple grid: the tile was 748px (73% canvas, ~22px above center) on the 1024 frame, because it was built around the under-scaled traced glyph. Regenerate from the corrected 1024 master (tile 824x824 centered, glyph/tile 39.3%x52.4%) with @2x variants intact. Geometry verified by scripts/regen_app_icons.sh self-check and test_icns.cpp.
…t API (#902) * Add gray release update check via snapmaker-config config/get API - POST /config/get runs before the static version.json check and falls back to it on any failure - appName encodes the platform (snapmaker-orca-win / snapmaker-orca-mac), deviceId is the gray bucketing key - sends the SM account JWT in the Authorization header when the user is logged in - response parsing reuses the existing update flow (release_type gate, Semver compare, force upgrade) - endpoint overridable via orca_config_api_url for testing * Address review: guard token transport, fall back on malformed payloads - default endpoint is the https prod URL; the http dev gateway is used only in BBL_INTERNAL_TESTING builds (per-machine override still wins via orca_config_api_url) - never attach Authorization over non-https URLs; internal testing builds are the only exception because the dev gateway has no TLS - malformed gray payloads (missing release_type / unparsable version / missing file_url / unsupported platform_type) now fall back to the static version.json check instead of silently suppressing the update notification - add a 30s total request timeout so a stalled transfer still triggers the fallback * Address review round 2: platform cross-check, quiet normal-path logging - reject payloads whose platform_type does not match the build's platform (same mapping as appName), falling back to the static channel instead of offering another platform's installer - normal-path request marker logs at info so routine check-in traffic does not raise the release warning baseline; failure paths stay at warning
DynamicPrintConfig::set_num_filaments does not resize filament vectors (the filament option key list is never initialized), so the three multi-filament BedTemperature tests silently ran with a single filament: region extruder IDs were clamped back to 1, the print sliced single-extruder, and the max-bed-temperature assertions failed with the first extruder's value (35 instead of 65). Configure the filament count the way a loaded preset does: filament count is derived from the filament_diameter data (Preset.cpp), so deserialize filament_diameter with two values instead of calling set_num_filaments. Note the vector separator must be a comma; a semicolon silently parses as a single value.
Print uses a defaulted constructor and declared Vec3d m_origin without an initializer, so the member holds indeterminate values until set_plate_origin() is called. Linux GCC flags the wipe-tower read sites with -Wmaybe-uninitialized, and CLI or test paths that slice before an origin is set could consume garbage coordinates. Initialize it to zero in the declaration, matching the other Print members such as m_modified_count and m_support_used.
* feat: add two-level vendor-grouped filament dropdown in sidebar
Replace the flat filament preset list with a grouped popup while keeping
the existing selection pipeline untouched
* refactor: extract pluggable sort strategies for the filament dropdown
* fix: keep parent section text in filament dropdown
* feat: auto-open the selected filament group
* feat: sort filament names by character class
* fix: treat placeholder filament vendor as missing
+ trim filament dropdown comments
* fix: merge case variants of a vendor into one group
* refactor: rework filament popup and its ordering
+ drop character-class filament ordering;
+ restructure filament popup rendering;
+ harden filament popup lifetime and arithmetic;
* Add grouped filament dropdown sorting
- Add grouped two-level filament popup with project, user, and system sections.
- Add vendor and filament sorting strategies with Snapmaker-only TopN ordering.
- Load filament_topn.json once per process with validation and fallback behavior.
- Improve popup dismissal, submenu positioning, GTK motion handling, and lifecycle safety.
- Add deterministic sorting and grouped-row mapping tests.
- Document updater-managed TopN deployment and restart semantics.
* feat: scroll submenu selection to top on open
Add ensure_selection_at_top() and call it from openSelectionGroup()
after autoPosition(), so the submenu opens with the selected filament
row at the top of the viewport instead of at the list start. Restore
the selection on the submenu before positioning so the scroll offset
targets the row the user actually picked.
* Fix macOS filament popup dismissal
* fix: handle macOS reposted clicks around popups
wxOSX re-delivers an outside click after a transient popup dismisses,
and that repost can hit the wrong control and reopen the combo. Track
left-down events by timestamp and screen position in an event filter
and consume the repost at the filament combo boundary. Also flag
outside-click dismissals in FilamentDropDown so the override releases
native mouse capture even while the submenu is visible.
* fix: correct sidebar section collapse and layout
Toggle printer/filament/mixed sections from the shown state instead
of probing MaxHeight, and show the filament panel again when the AMS
list syncs. Give the nozzle notebook a DoGetBestSize that includes
page content so the sizer reserves its height and following panels no
longer overlap it.
* fix: drop ClickRecord default member initializers to fix macOS build
The nested ClickRecord struct's default member initializers are rejected by clang
when the struct is used as the element type of an inline static std::array member
(default member initializer needed within the enclosing class definition outside of
member functions). The fields are always assigned before use, and value-initializing
s_history via {} already zero-initializes them, so the initializers are redundant.
* fix: dismiss submenu when pointer moves onto root popup
The submenu counted the root popup as part of its own tree, so it
refused to dismiss while the pointer was over the root. That left a
stale list behind when switching rows and blocked root-item clicks
such as Add/Remove filaments and materials on macOS.
Restrict the submenu's "over popup tree" check to its own window and
the shared anchor; the root popup keeps its existing tree (root,
visible submenu, anchor).
* feat: load Snapmaker filament order from allow-list
- Read sections.filament_order from filament_allow_list.json\n- Remove filament_topn deployment and rename the filament order model\n- Preserve Snapmaker-only priority sorting and update parser tests
* fix: track macOS reposted clicks by value instead of event pointers
The filter that consumes wxOSX's reposted outside click stored a raw
const wxEvent* per recorded click, cleared records by pointer identity and
had no expiry. A repost that landed on any other control left its record
behind permanently, so the 16-slot history filled up and detection stopped,
and a recycled event address could swallow a legitimate first click.
- store timestamp, screen position and observation time only, so no event
pointer outlives the event it points to
- retire the matching record on the first hit, so one physical click can
consume at most one repost, and expire stale records after two seconds
- decide in FilterEvent, which runs before the handler chain, so the repost
never reaches the combo; the target check stays deliberately
target-agnostic and is documented in place
- hold the filter in a std::unique_ptr with an owner count: install it for
the first filament combo and RemoveFilter/reset it when the last one is
destroyed, instead of leaking a raw new until process exit
- make the detector a private nested class so it reads combo state without
a public accessor; macOS-only code (#ifdef __WXOSX__)
The macOS behaviour is unchanged: a repost that reaches a closed filament
combo is still consumed, so the dismissed menu does not reopen.
* fix: clamp filament popup scroll offset after screen-bottom resize
autoPosition() shrinks the popup when it would extend past the bottom of the
display, and then scrolled it with its own clamp that used the selected item
index as a visible row index and only clamped one side. Group folding and
submenu filtering make those two differ, so the offset could fall below the
content height and paint every row above the viewport: an empty popup that
only recovered after a wheel or a drag.
- scroll with ensure_selection_at_top(), the rule the group-switch path
already uses; it maps the selection to a visible row (a folded member maps
to its group header) and clamps both sides
- also enforce the plain scroll invariant the wheel and drag paths use
(offset.y between size.y - rowSize.y * count and 0), so the branch stays
correct even when the selection maps to no row at all
* fix: unify filament prefix matching and make the order rules testable
The same concept was spelled two ways: the popup stripped a vendor prefix
case-sensitively for display while the configured order matched product names
case-insensitively, so a casing drift kept the prefix visible in the submenu
but still matched the ordered entry. Both sides now ignore case, and the rules
that were reachable only through the GUI became pure functions with tests.
- add filament_product_key(): the preset name without vendor prefix or printer
suffix, matching on the same word boundary and ignoring ASCII case
- add choose_allow_list_copy(): the deployed user allow-list when that copy
exists, otherwise the shipped one, so the disk fallback is verifiable
without a build
- preset_filament_product() and the allow-list path now delegate to those
helpers instead of open-coding the rules in the GUI translation unit
- strip_group_prefix() ignores case, like the product matching it mirrors
- tests: product key, allow-list copy policy, case-drift stripping, plus
further allow-list parse-rejection cases and non-ASCII/repeated product names
Removals the same pass exposed:
- the split-item render path (style bit, context struct, painter) could not be
reached: no caller ever set that bit
- the four set_*_sorter entry points had no callers; the default sorters are
still created in the constructor, so the built-in ordering is unchanged
- a doc comment left behind when the vendor sorter moved out of the header
- FilamentDropDown takes its rows by const reference (the member is a copy)
- the wx find_package block in the test CMakeLists now sits with the other
find_package calls instead of after catch_discover_tests
* refactor: document the filament popup headers and tighten the popup constructor
- Mark FilamentDropDown(const std::vector<Item> &) explicit: both call sites
build it through std::make_unique, so no implicit conversion is in use.
- Annotate FilamentDropDown's submenu and root back-pointers as non-owning, and
point at mainDropDownWeak as the liveness-checked accessor for the same root.
- Fill in the missing Doxygen briefs on PlaterFilamentComboBox's Section,
PopupRow and its 11 undocumented helpers, matching the sibling headers.
---------
Co-authored-by: zhongzhipan <zhongzhipan@snapmaker.com>
Co-authored-by: zhangzhend0ng <zzdong0119@foxmail.com>
* feat: add two-level vendor-grouped filament dropdown in sidebar
Replace the flat filament preset list with a grouped popup while keeping
the existing selection pipeline untouched
* refactor: extract pluggable sort strategies for the filament dropdown
* fix: keep parent section text in filament dropdown
* feat: auto-open the selected filament group
* feat: sort filament names by character class
* fix: treat placeholder filament vendor as missing
+ trim filament dropdown comments
* fix: merge case variants of a vendor into one group
* refactor: rework filament popup and its ordering
+ drop character-class filament ordering;
+ restructure filament popup rendering;
+ harden filament popup lifetime and arithmetic;
* Add grouped filament dropdown sorting
- Add grouped two-level filament popup with project, user, and system sections.
- Add vendor and filament sorting strategies with Snapmaker-only TopN ordering.
- Load filament_topn.json once per process with validation and fallback behavior.
- Improve popup dismissal, submenu positioning, GTK motion handling, and lifecycle safety.
- Add deterministic sorting and grouped-row mapping tests.
- Document updater-managed TopN deployment and restart semantics.
* feat: scroll submenu selection to top on open
Add ensure_selection_at_top() and call it from openSelectionGroup()
after autoPosition(), so the submenu opens with the selected filament
row at the top of the viewport instead of at the list start. Restore
the selection on the submenu before positioning so the scroll offset
targets the row the user actually picked.
* Fix macOS filament popup dismissal
* fix: handle macOS reposted clicks around popups
wxOSX re-delivers an outside click after a transient popup dismisses,
and that repost can hit the wrong control and reopen the combo. Track
left-down events by timestamp and screen position in an event filter
and consume the repost at the filament combo boundary. Also flag
outside-click dismissals in FilamentDropDown so the override releases
native mouse capture even while the submenu is visible.
* fix: correct sidebar section collapse and layout
Toggle printer/filament/mixed sections from the shown state instead
of probing MaxHeight, and show the filament panel again when the AMS
list syncs. Give the nozzle notebook a DoGetBestSize that includes
page content so the sizer reserves its height and following panels no
longer overlap it.
* fix: drop ClickRecord default member initializers to fix macOS build
The nested ClickRecord struct's default member initializers are rejected by clang
when the struct is used as the element type of an inline static std::array member
(default member initializer needed within the enclosing class definition outside of
member functions). The fields are always assigned before use, and value-initializing
s_history via {} already zero-initializes them, so the initializers are redundant.
* fix: dismiss submenu when pointer moves onto root popup
The submenu counted the root popup as part of its own tree, so it
refused to dismiss while the pointer was over the root. That left a
stale list behind when switching rows and blocked root-item clicks
such as Add/Remove filaments and materials on macOS.
Restrict the submenu's "over popup tree" check to its own window and
the shared anchor; the root popup keeps its existing tree (root,
visible submenu, anchor).
* feat: load Snapmaker filament order from allow-list
- Read sections.filament_order from filament_allow_list.json\n- Remove filament_topn deployment and rename the filament order model\n- Preserve Snapmaker-only priority sorting and update parser tests
* fix: track macOS reposted clicks by value instead of event pointers
The filter that consumes wxOSX's reposted outside click stored a raw
const wxEvent* per recorded click, cleared records by pointer identity and
had no expiry. A repost that landed on any other control left its record
behind permanently, so the 16-slot history filled up and detection stopped,
and a recycled event address could swallow a legitimate first click.
- store timestamp, screen position and observation time only, so no event
pointer outlives the event it points to
- retire the matching record on the first hit, so one physical click can
consume at most one repost, and expire stale records after two seconds
- decide in FilterEvent, which runs before the handler chain, so the repost
never reaches the combo; the target check stays deliberately
target-agnostic and is documented in place
- hold the filter in a std::unique_ptr with an owner count: install it for
the first filament combo and RemoveFilter/reset it when the last one is
destroyed, instead of leaking a raw new until process exit
- make the detector a private nested class so it reads combo state without
a public accessor; macOS-only code (#ifdef __WXOSX__)
The macOS behaviour is unchanged: a repost that reaches a closed filament
combo is still consumed, so the dismissed menu does not reopen.
* fix: clamp filament popup scroll offset after screen-bottom resize
autoPosition() shrinks the popup when it would extend past the bottom of the
display, and then scrolled it with its own clamp that used the selected item
index as a visible row index and only clamped one side. Group folding and
submenu filtering make those two differ, so the offset could fall below the
content height and paint every row above the viewport: an empty popup that
only recovered after a wheel or a drag.
- scroll with ensure_selection_at_top(), the rule the group-switch path
already uses; it maps the selection to a visible row (a folded member maps
to its group header) and clamps both sides
- also enforce the plain scroll invariant the wheel and drag paths use
(offset.y between size.y - rowSize.y * count and 0), so the branch stays
correct even when the selection maps to no row at all
* fix: unify filament prefix matching and make the order rules testable
The same concept was spelled two ways: the popup stripped a vendor prefix
case-sensitively for display while the configured order matched product names
case-insensitively, so a casing drift kept the prefix visible in the submenu
but still matched the ordered entry. Both sides now ignore case, and the rules
that were reachable only through the GUI became pure functions with tests.
- add filament_product_key(): the preset name without vendor prefix or printer
suffix, matching on the same word boundary and ignoring ASCII case
- add choose_allow_list_copy(): the deployed user allow-list when that copy
exists, otherwise the shipped one, so the disk fallback is verifiable
without a build
- preset_filament_product() and the allow-list path now delegate to those
helpers instead of open-coding the rules in the GUI translation unit
- strip_group_prefix() ignores case, like the product matching it mirrors
- tests: product key, allow-list copy policy, case-drift stripping, plus
further allow-list parse-rejection cases and non-ASCII/repeated product names
Removals the same pass exposed:
- the split-item render path (style bit, context struct, painter) could not be
reached: no caller ever set that bit
- the four set_*_sorter entry points had no callers; the default sorters are
still created in the constructor, so the built-in ordering is unchanged
- a doc comment left behind when the vendor sorter moved out of the header
- FilamentDropDown takes its rows by const reference (the member is a copy)
- the wx find_package block in the test CMakeLists now sits with the other
find_package calls instead of after catch_discover_tests
* refactor: document the filament popup headers and tighten the popup constructor
- Mark FilamentDropDown(const std::vector<Item> &) explicit: both call sites
build it through std::make_unique, so no implicit conversion is in use.
- Annotate FilamentDropDown's submenu and root back-pointers as non-owning, and
point at mainDropDownWeak as the liveness-checked accessor for the same root.
- Fill in the missing Doxygen briefs on PlaterFilamentComboBox's Section,
PopupRow and its 11 undocumented helpers, matching the sibling headers.
* fix: keep the allow-list loader diagnostic distinct and pin its data contract
- FilamentOrder::from_file() reports why a configuration was rejected through an
optional out parameter, so load_filament_order() logs "cannot be opened" and
"has an invalid or empty configuration" as separate cases again instead of a
single merged warning.
- Add a test that parses the shipped resources/profiles/Snapmaker/filament/
filament_allow_list.json: without it a typo in that file drops the whole vendor
order at runtime without any visible error.
- Record the contracts that are held together by convention only: the section
labels parsed by section_from_header() (they must match what PresetComboBox
builds its headers with), the vendor-bucketing precondition of
SystemFilamentSorter::less(), and the upstream anchor-gap fix this fork has
already ported.
* fix: order filament names and vendors case-sensitively like the upstream table
Names and vendor labels that differ only in case were compared case-
insensitively, so they counted as equal and fell back to the base menu order.
Both now compare by code point, matching the Bambu comparator this popup was
ported from.
- default_name_less() and FilamentVendorSorter::less() compare with Cmp()
- vendor_rank() matches the Snapmaker and Generic buckets exactly; the
canonical spelling is still pinned before sorting by canonical_vendor(), and
the gate that activates the configured order keeps ignoring case so a
spelling drift cannot silently disable it
- tests pin the collation (A < _ < a, Zebra < apple), the equal-name tie-break
and the exact-spelling bucket
* docs: correct the filament popup briefs
Several briefs added to the popup headers described behaviour the code does
not have, so a reader would look for logic that is not there:
- on_key_down() opens and closes the popup; it does not forward events to it
- on_scroll_parent_move(), on_top_level_move() and on_top_level_size() only
dismiss, they do not reposition
- close_popup()'s flag emits the closeup event, it does not report a selection
- is_system_row() does not test selectability, only row identity
- preset_vendor() maps the historical "Bambu Lab" spelling to "Bambu"
- from_file() returns a reason fragment the caller logs with a component name
- FilamentSortItem holds the values a row is ordered by once the row is built
* fix: clear the submenu back-pointer when the root popup is destroyed
The submenu keeps a raw pointer to its root popup, and the submenu is created
after the root under the same wx parent. wx destroys children in creation
order (wxWindowBase::DestroyChildren() takes the first child of the list each
round), so inside the pair the submenu outlives the root: its autoPosition()
and OnDismiss() could read a root that is already gone.
The root's destructor now clears that pointer while the submenu is still
alive. The parallel wxWeakRef member invalidates itself, so both
representations agree again once the root is gone.
* fix: pin the filament submenu width and elide overflowing labels
The grouped filament popup sized its submenu from the longest preset name in
the active group, so switching groups resized the popup under the pointer. The
submenu now takes a fixed 200 DIP: a set fixed_width_dip outranks both the
content extent and the parent-width cap in messureSize(), the width padding of
the two scroll-bar branches no longer applies to it, and a clamp keeps the
popup inside the display that holds the anchor. The DIP scaling takes the
anchor's display, because the popup still sits at its creation position during
the first measurement and its own DPI can belong to another monitor.
Row labels are measured with the drawing font before the overflow test, so a
row that does not fit is elided instead of clipped.
Checked with cl /Zs on FilamentDropDown.cpp and PlaterFilamentComboBox.cpp;
no GUI run.
* fix: place the filament submenu beside its parent
The submenu relied on wx's popup placement plus a clamp keyed off
the fixed width, which could leave it overlapping the parent. Place
it explicitly: flush to the parent's right edge, flipped to its left
edge when the display has no room there, and clamped vertically into
the display.
The child now takes its row height from the root popup, so a rescale
or a font change cannot leave it with a stale row height. Moving or
scrolling the root popup closes an open submenu through DismissAll():
a plain Dismiss() would be vetoed by the child's pointer guard while
the pointer still sits over the popup tree.
Checked with cl /Zs on FilamentDropDown.cpp and
PlaterFilamentComboBox.cpp; no GUI run.
* build: make the allow-list test define portable
---------
Co-authored-by: zhongzhipan <zhongzhipan@snapmaker.com>
Co-authored-by: zhangzhend0ng <zzdong0119@foxmail.com>
Merge upstream/main into build_platform_engineering (2026-09-30)
ZackaryShen
approved these changes
Sep 30, 2026
zackaree-shen
approved these changes
Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Landing the accumulated
build_platform_engineeringwork intomain(41 commits, including the #948 merge that first synced main into the branch on 2026-09-30).Main areas of change
Filament UI & mixed filament
Update / release infrastructure
config/getAPI (feature: Add gray release update check via snapmaker-config config/get API #902)macOS
Windows installer (NSIS)
Stability & protocol
Conflict status
Screenshots/Recordings/Graphs
Per-area screenshots available in the original PRs referenced above (#929, #943, #899, #902, #865, #852, #870, #877).
Tests
build_platform_engineeringthrough its own reviewed PR (see references above).main↔build_platform_engineeringverified conflict-free viagit merge-treebefore opening this PR.