Compare commits

..
Author SHA1 Message Date
Lam Wei Lun 2c97343811 Replace wxGridCellChoiceEditor::OnComboCloseUp() with wxGridCellChoiceEditor::DismissEditor() 2026-10-11 22:50:07 +08:00
Lam Wei Lun cff514cd15 build(wx): move to upstream wxWidgets 3.3.4
Orca pinned the SoftFever fork at branch v3.3.2. That tracks a moving tip and
leaves the 3.3.3 image-decoder buffer overflows unfixed. Build upstream
wxWidgets at tag v3.3.4 instead and carry the fork's one extra commit as an
in-tree patch.

- deps/wxWidgets: GIT_REPOSITORY -> wxWidgets/wxWidgets, GIT_TAG -> v3.3.4
- add 0002-clang-cl-sibling-compiler-prefix.patch (fork PR #7, WIN32 only):
  the generated wxWidgetsConfig.cmake must search the sibling vc_/clang_
  install layout, because cl and clang-cl share an ABI but wx names the
  layout after the compiler that built it
- flatpak: upstream git source, tag v3.3.4, commit 6a42a4b9
- skill: update the pin, add the 3.3.3/3.3.4 sections, re-base the
  changes.txt line citations, and retire the wxGetTranslation() by-value rule
  (3.3.2 returned by value, 3.3.3 reverted to const wxString&)
2026-10-11 22:19:16 +08:00
HanifKoh 7369674d8f Add #pragma once to Headers Without an Include Guard (#16362) 2026-10-11 17:26:05 +08:00
HanifKoh 70da7e89bb Release the Slice Button's Mouse Capture When a Press Loses Its Release (#16358) 2026-10-11 15:49:04 +08:00
HanifKoh a671bf605d Dismiss the Slice-Plate Popup on Every Path Into the Filament Grouping Dialog (#16359)
#16284 closed the Slice-plate hover popup only when the Slice button opened
the filament grouping dialog. The dialog also opens from the plate's filament
grouping icon, the preview legend, slicing notification links and the send
dialog, and a popup still shown on those paths keeps the mouse capture on
macOS and starves the modal dialog of clicks. Dismiss it in
FilamentMapDialog::ShowModal() instead, so every path is covered.

The popup could also stay up on its own on macOS: an enter event stopped its
close timer, and when the timer fired with the cursor inside it returned
without re-arming, so without a later leave event the popup never closed. On
macOS the timer now keeps running while the popup is shown and closes it once
the cursor is outside. The popup also stops and frees its timer when destroyed.
2026-10-11 15:48:49 +08:00
SoftFever bae8f64b8b Complete and refine Simplified Chinese translations (#16356)
* Complete Simplified Chinese translations

* Fix Simplified Chinese 3D view, camera and extruder terms

* Use Chinese CAD view names on the navigator cube
2026-10-11 15:35:29 +08:00
HanifKoh 2df07b7ed8 Verify the TLS Certificate When Downloading a Cloud Plugin (#16253) 2026-10-11 14:25:50 +08:00
Klober81andRodrigo Faselli 295094a594 Pack bed texture and model into printer bundles (#15965)
* feat(preset): pack bed texture and model into printer bundles

Export copies bed_custom_texture and bed_custom_model into the .orca_printer zip under bed/. The manifest lists them in printer_asset. Import extracts each file into the bundle folder and rewrites the preset to that path. A missing file is skipped. A path that leaves the bundle directory is rejected. An old bundle with no printer_asset imports as before.

* Add includes flagged by clang-tidy misc-include-cleaner

* Gate bed asset packing on an export checkbox and key printer_asset per printer

The Export Configs checkbox can leave a custom bed out of a shared printer bundle. printer_asset is keyed by each printer_config zip entry, with files under bed/<n>/, so a later multi-printer bundle does not need a second manifest. The unshipped flat printer_asset object is not read.

---------

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-10-11 13:32:07 +08:00
Ian Bassi 210bc927bc Revert "Add Thai Localization (Educational / Hybrid Translation)" (#16349) 2026-10-10 22:39:45 -03:00
SoftFever 00cfb5b259 Refresh translation catalogs and fix xgettext warnings (#16346) 2026-10-11 08:45:44 +08:00
chettasit 2bd105b0ce Add Thai Localization (Educational / Hybrid Translation) (#15940)
Hi OrcaSlicer Team,I have completed the Thai localization for OrcaSlicer (over 7,000 lines).Please note: This is a "Hybrid Translation" designed specifically for educational purposes. Instead of direct literal translations (which often confuse beginners due to technical jargon), I have included brief explanations of the mechanical and physics principles directly within the UI strings.Example: For "TPU is not supported by AMS", the translation briefly explains why (because the material is soft, rubber-like, and will jam the long tubes).This approach was developed for the EDrobot project in Khon Kaen, Thailand to help STEM students and beginners understand 3D printing concepts immediately without a steep language barrier.[cite: 8] We focus on empowering students to learn robotics and 3D design effectively.[cite: 8]   I understand this makes some strings longer than standard localizations, but it has proven incredibly helpful for beginners. I would be honored if you consider adding this to the official release as the Thai language option. I am happy to maintain this file for future updates.Thank you for building such an amazing slicer!Best regards,Chetsit Srichan (EDrobot)
2026-10-11 08:34:19 +08:00
Ian BassiandRodrigo Faselli b81d761605 Refresh plate tab after bed type change (#16344)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-10-10 21:06:29 -03:00
6922edfc20 Fix Anycubic Vyper start G-code hotend wait (#16216)
* Fix Anycubic Vyper start gcode hotend wait

* Bump Anycubic profile version

---------

Co-authored-by: 陈诗健 <chenshijian@chenshijiandeMacBook-Air.local>
Co-authored-by: SoftFever <softfeverever@gmail.com>
Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
2026-10-11 07:34:11 +08:00
Javad Shafique 7ec147603f Default Bambu profiles to 3MF output (#14767) 2026-10-11 06:55:50 +08:00
jorymorrison 8ee9e5915a Fix Prusa CORE One pressure advance condition in filament profiles (#16093) 2026-10-11 06:39:53 +08:00
SoftFever cd5b786704 Add AnkerMake PLA+ filament profiles (Basic, Matte, Metallic, Silk, Glitter) (#16293)
# Description

Adds Anker's own AnkerMake PLA+ filament line to the Anker vendor
bundle: **Basic, Matte, Metallic, Silk, Glitter**. Until now the bundle
only had `Generic PLA+ @Anker`.

Each filament follows the existing Anker layout. A `@Anker base` profile
(`instantiation: false`, inherits `Generic PLA+ @Anker base`,
`filament_vendor: AnkerMake`) holds the settings. An instantiated
`@Anker` profile holds only `compatible_printers`. `filament_id` and
`setting_id` were minted with `scripts/orca_profile_tool.py
generate-id`, and `Anker.json` was re-indexed with `update-index`. The
Anker vendor version goes from `02.04.00.05` to `02.04.00.06`.

## Source of the values

The values come from the AnkerMake filament profiles bundled with
**eufyMake Studio** (Anker's PrusaSlicer 2.6 fork, AGPL-3.0, source at
https://github.com/eufymake/eufyMake-PrusaSlicer-Release), in
`profiles/Anker-ini/AnkerMake base/base.ini`. OrcaSlicer is also
AGPL-3.0.

Conversion notes:
- PrusaSlicer keys are mapped to their Orca equivalents. For example,
`bed_temperature`/`first_layer_bed_temperature` go to `hot_plate_temp*`
and `textured_plate_temp*`, `fan_below_layer_time` goes to
`fan_cooling_layer_time`, `slowdown_below_layer_time` goes to
`slow_down_layer_time`, and `extrusion_multiplier` goes to
`filament_flow_ratio`. Only keys that differ from the inherited `Generic
PLA+ @Anker base` chain are written.
- Where Studio sets retraction but leaves deretraction unset (Metallic,
Silk), `filament_deretraction_speed` is set equal to the retraction
speed. That matches Prusa's "0 = same as retract" meaning.
- **Max volumetric speed is new.** Studio ships
`filament_max_volumetric_speed = 0` (uncapped) for these filaments, and
Orca has no equivalent. The caps were derived from the peak flow that
Studio's own M5C "Fast" process pushes: Basic/Matte/Glitter 22 mm³/s,
Metallic 20, Silk 13. Please treat these as the least-validated numbers
in the PR.
- Studio has pressure advance disabled for these filaments, so no PA
values are carried over.
- Colour variants are not included because they differ only in colour.
`default_filament_colour` uses each family's base colour.
- `compatible_printers` follows Studio's `compatible_printers_condition`
for each family, limited to the 0.4/0.6 printers that `Generic PLA+
@Anker` covers:
  - Basic, Glitter: M5, M5 All-Metal, M5C at 0.4 and 0.6
- Silk: 0.4 on all three, plus M5C 0.6 and M5 All-Metal 0.6 (Studio
excludes M5 0.6)
  - Matte, Metallic: 0.4 only (Studio restricts these to 0.4)
- Studio also allows Basic on 0.2 nozzles. That variant is not added,
because the 0.2 Generic profiles use a separate low-flow cap.

# Screenshots/Recordings/Graphs

N/A (profile-only change).

## Tests

- `scripts/check_profile.sh` (local twin of the CI job, run against the
full profile tree with the nightly macOS validator) passes all five
checks: `profile_tool`, `validate_system`, `validate_slice`,
`validate_filament_subtypes`, `validate_custom` (fixtures v1.9.0 to
v2.4.2).
- `python3 -m unittest discover -s scripts/tests -t scripts`: OK.
- The submitter printed with these filament settings in eufyMake Studio
on the M5C's stock brass 0.4 nozzle without issues. This Orca conversion
has been loaded and sliced in Orca 2.4.2 (as user presets inheriting the
Anker generic PLA+) but not yet printed on brass. The printer now has a
different nozzle fitted.

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-10-11 06:26:26 +08:00
HanifKoh d715bb760c Let Plugin Windows Be Maximized on X11 (#16335)
wxGTK types every wxDialog as a dialog window, and Mutter offers no
maximize for anything but a normal window, so the maximize button and
shortcut did nothing on GNOME under X11. Modeless plugin windows are now
normal windows, kept off the taskbar as before. Modal ones stay dialogs
so the window manager keeps routing focus from the main window to them.
2026-10-11 04:40:56 +08:00
SoftFever 63918b44a8 Merge branch 'main' into anker-ankermake-pla-plus-filaments 2026-10-10 22:41:16 +08:00
mateuszandClaude Opus 5.5 64d09c9bca Add AnkerMake PLA+ filament profiles for Anker printers
Adds AnkerMake PLA+ Basic, Matte, Metallic, Silk and Glitter, converted from eufyMake Studio's bundled AnkerMake profiles (AGPL-3.0). Bumps the Anker vendor version to 02.04.00.06.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:13:00 -05:00
100 changed files with 25690 additions and 8269 deletions
+11 -7
View File
@@ -6,7 +6,7 @@ description: Use when writing, modifying, reviewing or debugging any OrcaSlicer
# OrcaSlicer wxWidgets GUI
This skill is how OrcaSlicer's wxWidgets GUI is written, changed, fixed and reviewed. Its core is
**wxWidgets 3.3.2 API usage** — the documented contracts, the per-platform behaviour and the known
**wxWidgets 3.3.4 API usage** — the documented contracts, the per-platform behaviour and the known
limitations of the wx version Orca pins — layered with the OrcaSlicer conventions, wrappers, custom
widget library and stable component designs a contributor must follow. Every reference file opens
with a numbered **Rules** checklist (what a diff is checked against), followed by sections that give
@@ -15,9 +15,12 @@ wrong → right pairs with the commit that fixed each one.
## Ground truth: the wx tree in deps/
Orca pins **wxWidgets 3.3.2** from the fork `github.com/SoftFever/Orca-deps-wxWidgets` (tag `v3.3.2`,
`deps/wxWidgets/wxWidgets.cmake`), built static (Flatpak: shared). Facts about this build that change
how you read the wx docs:
Orca pins upstream **wxWidgets 3.3.4** (`github.com/wxWidgets/wxWidgets`, tag `v3.3.4`,
`deps/wxWidgets/wxWidgets.cmake`), built static (Flatpak: shared). Two in-tree patches are applied: an
Apple-only sRGB colour-component fix (`0001-macos-use-srgb-colour-components.patch`) and a Windows-only
clang-cl sibling-compiler search fix for the installed `wxWidgetsConfig.cmake`
(`0002-clang-cl-sibling-compiler-prefix.patch`); neither applies on Linux. Facts about this build that
change how you read the wx docs:
- **Linux builds against GTK3** (`DEP_WX_GTK3` defaults ON in `deps/CMakeLists.txt`). Target GTK3 on
both X11 and Wayland; GTK-guarded code must still compile on GTK2, an opt-out build Orca does not ship.
@@ -33,7 +36,7 @@ Look things up in the source the app is built from — it beats memory, and 3.3
WX=$(find -L deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets' 2>/dev/null | head -1)
# macOS: deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets Linux, Windows: deps/<tree>/dep_wxWidgets-prefix/...
# -L follows a worktree's deps/<tree> symlinked to the main checkout. Not a glob: zsh aborts on one that matches nothing.
# If deps are not built: git clone --depth 1 -b v3.3.2 https://github.com/SoftFever/Orca-deps-wxWidgets
# If deps are not built: git clone --depth 1 -b v3.3.4 https://github.com/wxWidgets/wxWidgets
grep -n "CaptureMouse" -A 30 $WX/interface/wx/window.h # documented contract (doxygen source)
grep -rn "@onlyfor\|not implemented" $WX/interface/wx/popupwin.h # documented platform limits
ls $WX/docs/doxygen/overviews/ # eventhandling.h, sizer.h, high_dpi.md, windowdeletion.h, ...
@@ -219,14 +222,15 @@ known class with a pitfall entry and a fixing commit.
any wx key event; Control+click arrives as a right-click.
- **Windows:** `IsDark()` and `wxSYS_COLOUR_*` follow the system app mode, not Orca's theme — use
`dark_mode()`; menu bitmaps follow `check_dark_mode()`; windows are not double-buffered by default in
3.3.2; `ProcessLeftDown` is never called for popups; a popup that must close with the frame calls
3.3.4; `ProcessLeftDown` is never called for popups; a popup that must close with the frame calls
`BindUnfocusEvent()`.
- **Linux GTK3:** dialogs without size hints collapse (only sizer-fitting calls, not `Fit()`, are
replayed at the first `Show()`); command events from a popup's children are not stopped at the popup
(MSW/macOS stop them); chained popups need `transient_for` set to the mapped parent right before
showing; native borders leak through custom widgets (`RemoveButtonBorder`/`RemoveInputBorder`).
- **Wayland:** no global pointer position, no window positioning, `wxClientDC`, `Update()` and `SetIcon`
do nothing, no floating AUI panes, GL is EGL only.
do nothing, AUI floating is disabled (wx 3.3.4 can drag a floating frame via system drag sessions, but
its docking hints still need global positions), GL is EGL only.
## Editing this skill
@@ -59,7 +59,7 @@ Contents: [Rules](#rules) · [wxColour](#wxcolour) · [System colours and appear
**Contract.** Constructors: `()`, `(r, g, b, a = wxALPHA_OPAQUE)`, `(unsigned long|long|int|unsigned int)` ("A
packed RGB value", `interface/wx/colour.h:98-101`), `(const wxString&|const char*|const wchar_t*)`;
`wxColour(bool) = delete` (`include/wx/colour.h:225-240`; `docs/changes.txt:246-248`: code "unintentionally
`wxColour(bool) = delete` (`include/wx/colour.h:225-240`; `docs/changes.txt:278-280`: code "unintentionally
and mistakenly using wxColour ctor from bool … doesn't compile any longer").
| Fact | Cite |
@@ -121,7 +121,7 @@ returned colour is always valid" (`interface/wx/settings.h:44-48`, `:398-407`).
| Method | Contract |
|---|---|
| `IsDark()` | "checks the appearance of the current application and not the other applications on the system, so under MSW … will return false even if dark mode is used system-wide unless the application opted in using dark mode using wxApp::MSWEnableDarkMode()" (`:332-346`). An incompatible 3.3 change (`docs/changes.txt:71-73`). |
| `IsDark()` | "checks the appearance of the current application and not the other applications on the system, so under MSW … will return false even if dark mode is used system-wide unless the application opted in using dark mode using wxApp::MSWEnableDarkMode()" (`:332-346`). An incompatible 3.3 change (`docs/changes.txt:77-79`). |
| `AreAppsDark()` (3.3.0) | system-wide app dark mode "even if it's not enabled for this particular application"; same as `IsDark()` off MSW (`:308-321`). |
| `IsSystemDark()` (3.3.0) | the "Windows mode", which can differ from the "app mode" (`:348-358`). |
| `IsUsingDarkBackground()` | luminance fallback, "generally not very useful to call directly" (`:360-370`). |
@@ -233,25 +233,25 @@ child handlers.
`@onlyfor{wxmsw}`, `interface/wx/app.h:1418-1465`):
- "experimental"; uses "undocumented, and unsupported by Microsoft, functions"; works on Windows 10 later than
v1809 (including LTSC 2019) and all Windows 11; testing before 20H1 (v2004) "has been limited".
- Flags: default follows the system ("dark mode is only used if it is the default mode for the applications on
the current system"); `DarkMode_Always` forces dark. **[source]** `DarkMode_Auto = 0` exists in
`include/wx/msw/app.h:48` although only `DarkMode_Always` is documented.
- Flags: `DarkMode_Auto` (default) follows the system ("dark mode is only used if it is the default mode
for the applications on the current system"); `DarkMode_Always` forces dark, `DarkMode_Never` forces
light. **[source]** the enum also names `DarkMode_Auto = 0` in `include/wx/msw/app.h:46-52`;
`DarkMode_Never` is new in 3.3.4 (documented at `interface/wx/app.h:1450-1454`).
- Returns `true` if enabled, `false` "most likely because the system doesn't support dark mode".
- Alternatives: the `msw.dark-mode` system option (1 = `MSWEnableDarkMode()`, 2 = `DarkMode_Always`, settable by
environment variable from outside the app, `interface/wx/sysopt.h:85-89`), or `SetAppearance(System|Dark)`.
- Known limitations (`interface/wx/app.h:1434-1448`): anything `TaskDialog()`-based has no dark mode —
`wxMessageBox()`, `wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`, simple `wxAboutBox()` (wx suggests
`wxGenericMessageDialog`/`wxGenericProgressDialog`); common-dialog wrappers `wxColourDialog`, `wxFindReplaceDialog`,
`wxFontDialog`, `wxPageSetupDialog`, `wxPrintDialog`; `wxTimePickerCtrl`, `wxDatePickerCtrl`, `wxCalendarCtrl`
stay light; toolbar items with `wxToolBar::SetDropdownMenu()` draw the drop-down "almost invisible".
environment variable from outside the app, `interface/wx/sysopt.h:85-89`), or `SetAppearance(System|Light|Dark)`.
- Known limitations (`interface/wx/app.h:1442-1448`): `wxTimePickerCtrl`, `wxDatePickerCtrl` and
`wxCalendarCtrl` stay light; `wxPrintDialog` on Windows 10 stays light; MDI applications are not
recommended in dark mode. `wxMessageDialog` gained dark mode in 3.3.3 (#26570) and the common dialogs
in 3.3.4 (#26780), so the older "anything `TaskDialog()`-based and the common dialogs stay light" note
no longer applies.
`AppearanceResult wxApp::SetAppearance(Appearance::System|Light|Dark)` (3.3.0, `interface/wx/app.h:1152-1190`):
`AppearanceResult wxApp::SetAppearance(Appearance::System|Light|Dark)` (3.3.0, `interface/wx/app.h:1130-1187`):
GTK/macOS follow the system by default and the call is immediate and "affects all the existing windows as well
as any windows created after this call"; "Under MSW, the default appearance is always light" and an app that
wants to follow the system must call it with `Appearance::System`; "the appearance can be only set before any
windows are created and calling this function too late will return AppearanceResult::CannotChange" (only wxMSW
returns it);
`Failure` e.g. "because `GTK_THEME` is defined".
wants to follow the system must call it with `Appearance::System`; on MSW it maps to `MSWEnableDarkMode()` and
returns `Ok`/`Failure` (`interface/wx/app.h:1166-1169`). `Failure` e.g. "because `GTK_THEME` is defined";
`AppearanceResult::CannotChange` is "currently never returned" (`interface/wx/app.h:1151, 1179-1183`).
`wxDarkModeSettings` (`interface/wx/msw/darkmode.h:30-111`), passed to `MSWEnableDarkMode()`: `GetColour(wxSystemColour)`
(defaults "not documented and are subject to change"; the doc example names `0x202020` as the default background);
@@ -260,34 +260,40 @@ returns it);
which "doesn't look very well in dark mode"; the base returns grey.
**[source] facts the docs do not state:**
- `SetAppearance` on MSW returns `CannotChange` when any TLW exists **or `MSWEnableDarkMode` was already called**
(`gs_appMode != AppMode_Default`); `SetAppearance(Light)` returns `Ok` without doing anything
(`src/msw/darkmode.cpp` `wxApp::SetAppearance`). macOS `System` sets `NSApp.appearance` to
`[NSAppearance currentAppearance]` rather than nil, pinning the current look (`src/osx/cocoa/utils.mm:485-513`);
GTK3 maps to the portal colour-scheme machinery; GTK2 always returns `Failure` (`src/gtk/app.cpp:355-381`).
- `MSWEnableDarkMode` may be called any time, but dark title bars are applied only to TLWs **created** afterwards
- As of 3.3.3 (#26516) and 3.3.4 (#26734), MSW dark mode can be switched at runtime: `SetAppearance()` maps to
`MSWEnableDarkMode()` and returns `Ok`/`Failure`, never `CannotChange` (`src/msw/darkmode.cpp:354-375`).
`MSWEnableDarkMode()` re-applies the mode to an existing window by sending it `WM_SYSCOLORCHANGE` (`:345-349`),
and the window restyles through `wxMSWDarkMode::HasChanged()`/`NotifySysColorChange()`
(`src/msw/window.cpp:5337, 5528-5534`), so a control created before the switch is re-themed too. macOS `System`
sets `NSApp.appearance` to `[NSAppearance currentAppearance]` rather than nil, pinning the current look
(`src/osx/cocoa/utils.mm:485-513`); GTK3 maps to the portal colour-scheme machinery; GTK2 always returns
`Failure` (`src/gtk/app.cpp:355-381`).
- `MSWEnableDarkMode` may be called any time; dark title bars are applied when a TLW is created
(`EnableForTLW` at the end of TLW creation, `src/msw/toplevel.cpp:513`) and controls are dark-enabled at creation
(`MSWCreateControl` → `AllowForWindow` and, for some controls, `SetForegroundColour(LISTBOXTEXT)`,
`src/msw/control.cpp:133-140`). wx cannot flip existing windows — the reason `SetAppearance` refuses late calls.
`src/msw/control.cpp:133-140`).
- wx takes ownership of the settings pointer (`wxDarkModeModule::SetSettings`) — allocate it with `new`.
- `msw.dark-mode` is read in `wxApp::Initialize` (`src/msw/app.cpp:492-494`): a user's `WX_MSW_DARK_MODE`
environment variable enables wx dark mode before Orca's own call.
- wx's owner-drawn menu path keys on `wxMSWDarkMode::IsActive()` (`src/msw/menuitem.cpp` `wxMenuItem::OnDrawItem`,
`GetColourToUse`; `src/msw/menu.cpp`; menu-bar UAH drawing in `src/msw/darkmode.cpp`).
3.3.x dark-mode fix log: 3.3.2 wxMSW (`docs/changes.txt:297-308`: checkbox accessibility in dark mode, rendering of
several controls, toolbar, menus; also "Revert use of WS_EX_COMPOSITED"), 3.3.1 (`:358-372`: wxStaticBitmap-in-notebook
crash, disabled wxButton bitmaps and wxStaticText, notebook high-contrast background, wxDataViewCtrl light-mode
border regression, selected toolbar buttons, wxComboCtrl, wxTE_RICH wxTextCtrl), 3.3.0 (`:385` "Add experimental dark
mode support to wxMSW"); XRC dark colour variants (`:494`).
3.3.x dark-mode fix log: 3.3.4 wxMSW (`docs/changes.txt:349-358`: dynamic dark mode, dark mode for
wxFontPickerCtrl, wxNotebook, wxSpinButton and the common dialogs), 3.3.3 (`:433` "Implement support for
switching between light/dark modes"; `:426` wxMessageDialog, `:429` combo/listbox on Windows 11, `:431`
wxCheckBox label in screen readers), 3.3.2 (`:492-501`: checkbox accessibility in dark mode, rendering of
several controls, toolbar, menus; also "Revert use of WS_EX_COMPOSITED"), 3.3.1 (`:553-565`:
wxStaticBitmap-in-notebook crash, disabled wxButton bitmaps and wxStaticText, notebook high-contrast
background, wxDataViewCtrl light-mode border regression, selected toolbar buttons, wxComboCtrl, wxTE_RICH
wxTextCtrl), 3.3.0 (`:578` "Add experimental dark mode support to wxMSW"); XRC dark colour variants (`:687`).
**OrcaSlicer.** `GUI_App::on_init_inner` (`#ifdef __WINDOWS__`) calls `MSWEnableDarkMode(DarkMode_Auto)` and then
`NppDarkMode::InitDarkMode(init_dark_color_mode, init_sys_menu_enabled)`. wx's call exists only so that wx-drawn
menus get dark borders; NppDarkMode does the theming (title bars, explorer theme, scrollbars, list headers) because
it can switch live and wx cannot. The code comment "Orca: todo switch to native dark mode support in wxWidgets and
remove NppDarkMode" records the intent; the blocker is that a live Preferences toggle would become restart-only
(`SetAppearance` returns `CannotChange` after startup, existing windows cannot be restyled) and TaskDialog/common
dialogs/pickers stay light anyway — which is also why Orca has its own `MsgDialog` family and `ProgressDialog`.
`NppDarkMode::InitDarkMode(init_dark_color_mode, init_sys_menu_enabled)`. wx's call exists so that wx-drawn
menus get dark borders; NppDarkMode does Orca's theming (title bars, explorer theme, scrollbars, list headers).
The code comment "Orca: todo switch to native dark mode support in wxWidgets and remove NppDarkMode" records the
intent. The old blocker — a live Preferences toggle would be restart-only because `SetAppearance` returned
`CannotChange` and existing windows could not be restyled — is gone as of 3.3.3/3.3.4 (above). Orca still keeps
NppDarkMode, and Orca's own `MsgDialog` family and `ProgressDialog` remain its message/progress UI.
**[source]** consequences of `DarkMode_Auto`: wx's internal "dark" stays `AllowDark` and follows the **system app
mode**; NppDarkMode's later `SetPreferredAppMode` call does not touch wx's `gs_appMode`. So with Windows light +
Orca dark, wx's dark machinery (menus, `GetColour()`, `IsDark()`) stays off; with Windows dark + Orca light it is
@@ -312,11 +318,15 @@ on: `GetColour()` returns the dark palette, new native controls are dark-enabled
```
Cite: bf397a0632 (`GUI_App.cpp`, `GUI_App::on_init_inner`).
- **Rule:** Do not call `wxTheApp->SetAppearance(...)` in Orca.
**Why:** MSW returns `CannotChange` (Orca already called `MSWEnableDarkMode`), GTK2 fails, and macOS pins
**Why:** MSW would switch wx's own mode out of step with NppDarkMode and `dark_color_mode` (wx's mode is driven
by `MSWEnableDarkMode(DarkMode_Auto)` and follows the system), GTK2 fails, and macOS pins
`NSApp.appearance` while `GUI_App::dark_mode()` keeps reading the system `AppleInterfaceStyle` — the two would
disagree.
- **Rule:** Do not show wx's TaskDialog-based or common dialogs where dark mode matters; use `MessageDialog` and
friends (`MsgDialog.hpp`, see `references/windows-dialogs.md`).
- **Rule:** Do not show wx's TaskDialog-based or common dialogs; use `MessageDialog` and friends
(`MsgDialog.hpp`, see `references/windows-dialogs.md`).
**Why:** Orca's dialogs are owner-drawn and consistent with the rest of the UI. The old dark-mode gap
closed — `wxMessageDialog` gained dark mode in 3.3.3 (#26570) and the common dialogs in 3.3.4 (#26780) —
but the convention stands.
## Window colours, inheritance and native-control limits
@@ -371,7 +381,7 @@ macOS paints the system grey.
| Port | Behaviour |
|---|---|
| MSW | `wxButton`/`wxToggleButton` `SetBackgroundColour`/`SetForegroundColour` switch the native button to `BS_OWNERDRAW` (`src/msw/anybutton.cpp` `wxAnyButton::MakeOwnerDrawn`, `:1324-1390`) — the colour works but native theming is gone. `wxCheckBox`/`wxRadioButton` foreground makes them owner-drawn when themes are active (`src/msw/control.cpp` `MSWMakeOwnerDrawnIfNecessary`); 3.3.2 fixed checkbox accessibility in that mode (`docs/changes.txt:299-300`). Static-text-like children paint the ancestor brush only if that ancestor's background is inheritable. |
| MSW | `wxButton`/`wxToggleButton` `SetBackgroundColour`/`SetForegroundColour` switch the native button to `BS_OWNERDRAW` (`src/msw/anybutton.cpp` `wxAnyButton::MakeOwnerDrawn`, `:1324-1390`) — the colour works but native theming is gone. `wxCheckBox`/`wxRadioButton` foreground makes them owner-drawn when themes are active (`src/msw/control.cpp` `MSWMakeOwnerDrawnIfNecessary`); 3.3.2 fixed checkbox accessibility in that mode (`docs/changes.txt:492-493`). Static-text-like children paint the ancestor brush only if that ancestor's background is inheritable. |
| macOS | `SetBackgroundColour` reaches the NSView only if it `respondsToSelector:setBackgroundColor:` and the style is not `wxBG_STYLE_TRANSPARENT` (`src/osx/cocoa/window.mm:3514-3531`): **NSButton (`wxButton`) ignores the background colour**. Foreground uses `setTextColor:` when the view has it (`:3885-3887`); stock NSButton has none. `wxStaticText` is an NSTextField with `setDrawsBackground:NO` — its background is never painted (`src/osx/cocoa/stattext.mm:154`). `wxTextCtrl`'s `setEnabled:` resets the text colour: multi-line (`wxNSTextView`) always, to `controlTextColor`/`disabledControlTextColor`; single-line (`wxNSTextField`) when it does not draw its background, to `controlTextColor`/`secondarySelectedControlColor` (`src/osx/cocoa/textctrl.mm`). |
| GTK3 | `SetBackgroundColour/SetForegroundColour/SetFont` install a per-widget CSS provider `*{color:..;background:..;font:..}` at `GTK_STYLE_PROVIDER_PRIORITY_APPLICATION` (`src/gtk/window.cpp` `wxWindowGTK::GTKApplyStyle`); `wxButton`/`wxCheckBox` apply it to their inner label too (`src/gtk/button.cpp:325-335`, `src/gtk/checkbox.cpp:233-237`). User CSS (priority USER) can still override. |
| GTK2 | colours applied with `gtk_widget_modify_style` — pixmap-engine themes may ignore them. |
@@ -106,7 +106,7 @@ The general wx rule is that setters do not send events. The exceptions are the b
| `wxTextEntry::SetValue(s)` | **yes**, one `wxEVT_TEXT`, even when `s` equals the current text | `interface/wx/textentry.h:539-542`; [source] `src/common/textentrycmn.cpp:236-254`, `src/msw/textctrl.cpp:1133-1145` |
| `wxTextEntry::ChangeValue(s)` | no | `interface/wx/textentry.h:177-178` |
| `wxTextEntry::Clear()` | yes (= `SetValue("")`) | `interface/wx/textentry.h:193-194` |
| `wxTextCtrl::SetLabel/SetLabelText` | nothing at all (3.3) | `docs/changes.txt:111-113`; `src/common/textcmn.cpp:934-937` |
| `wxTextCtrl::SetLabel/SetLabelText` | nothing at all (3.3) | `docs/changes.txt:117-119`; `src/common/textcmn.cpp:934-937` |
| `SetSelection/SetStringSelection` on wxChoice, wxComboBox, wxListBox, wxRadioBox | no | `interface/wx/ctrlsub.h:102-103,126` |
| `wxComboBox::SetValue` | `wxEVT_TEXT` if editable; none with `wxCB_READONLY` | `interface/wx/combobox.h:256-272` |
| `wxComboBox::Popup()/Dismiss()` | DROPDOWN/CLOSEUP, except on wxOSX | `interface/wx/combobox.h:279-281,292-294` |
@@ -184,7 +184,7 @@ Pitfalls:
ctrl->GetTextCtrl()->SetLabel(s); // Wrong: no-op
ctrl->GetTextCtrl()->ChangeValue(s); // Right (or SetValue if listeners must react)
```
Cite: `docs/changes.txt:111-113`.
Cite: `docs/changes.txt:117-119`.
- **Rule:** Handle `wxEVT_TEXT_ENTER` without `Skip()` unless you want the default button activated as well.
**Why:** a skipped Enter falls through to the dialog's default button and closes it.
```cpp
@@ -213,7 +213,7 @@ Contract:
verbatim, and `wxControl::EscapeMnemonics()` escapes it (`interface/wx/control.h:174-195, 379-387`). This applies
to `wxStaticText`, `wxCheckBox`, `wxRadioButton`, `wxButton` labels and to book page titles: all `wx*book`
classes interpret mnemonics in page text (`interface/wx/bookctrl.h:141-147`), and since 3.3 wxListbook and
wxChoicebook do too (`docs/changes.txt:128-130`).
wxChoicebook do too (`docs/changes.txt:134-136`).
- `SetLabelMarkup()` needs well-formed markup or the label "won't be shown at all" (returns false, keeps the old
label). A bare `&` is still a mnemonic. Multi-line markup works only on GTK and macOS; the generic version used on
MSW handles single lines (`interface/wx/control.h:338-347`). Quote untrusted text with
@@ -383,10 +383,10 @@ Pitfalls:
- Virtual list (`wxLC_REPORT|wxLC_VIRTUAL`): call `SetItemCount()` and override `OnGetItemText` (optionally
`OnGetItemImage`/`OnGetItemAttr`) (`interface/wx/listctrl.h:128-134`). `EditLabel()` asserts without
`wxLC_EDIT_LABELS` (`docs/changes.txt:68-69`).
`wxLC_EDIT_LABELS` (`docs/changes.txt:74-75`).
- Images: prefer `SetImages(std::vector<wxBitmapBundle>)`; `wxImageList` is discouraged
(`interface/wx/withimages.h:21-40`) and is measured in physical pixels in 3.3 (`docs/changes.txt:85-88`); calling
its methods on an invalid (unsized) list now asserts, i.e. fails silently in Orca (`docs/changes.txt:53-56`).
(`interface/wx/withimages.h:21-40`) and is measured in physical pixels in 3.3 (`docs/changes.txt:91-94`); calling
its methods on an invalid (unsized) list now asserts, i.e. fails silently in Orca (`docs/changes.txt:59-62`).
`Assign*` transfers ownership, `Set*ImageList` does not (`interface/wx/withimages.h:90-110`). Sizing:
`references/dpi-bitmaps-fonts.md`.
- `wxTreeCtrl`: `SelectItem` emits events ([table](#events-from-programmatic-changes)).
@@ -402,7 +402,7 @@ Contract:
selection's blocks).
- `FreezeTo(row, col)` returns false (an assert, silent in Orca) for out-of-range values, merged cells or the native
header (`interface/wx/grid.h:5747-5772`); [source] also when rows/columns were reordered or drag-moving is enabled
(`src/generic/grid.cpp:5742-5750`). In 3.3 it freezes even when the grid is too small (`docs/changes.txt:48-51`).
(`src/generic/grid.cpp:5742-5750`). In 3.3 it freezes even when the grid is too small (`docs/changes.txt:54-57`).
- Editor contract: `EndEdit` must not modify the grid — it stores the value and returns true if it changed;
`ApplyEdit` writes it after `wxEVT_GRID_CELL_CHANGING` was not vetoed (`interface/wx/grid.h:610-638`). Editors,
renderers and attrs are ref-counted and the setters take ownership (`:1263-1305, 1739-1770, 2681-2701`).
@@ -492,7 +492,7 @@ All cites `interface/wx/dataview.h`. MSW uses the generic implementation; wxGTK
Other per-port facts: editing starts on a slow double-click or a platform key — "F2 is typical on Windows, Space
and/or Enter is common elsewhere" (2604-2607, 1915-1918); a custom renderer's `StartDrag()` is "Not yet supported"
(2717-2719); `RenderText()` should be used inside `Render()` so text matches native renderers (2708-2714). Calling
`Collapse()` from an event handler was fixed in 3.3.1 (`docs/changes.txt:346`; the generic re-check is described
`Collapse()` from an event handler was fixed in 3.3.1 (`docs/changes.txt:539`; the generic re-check is described
under [DVC control API](#wxdataviewctrl-control-api)).
OrcaSlicer: `ParamsViewCtrl` (`EditGCodeDialog`) and `DiffViewCtrl` (`UnsavedChangesDialog`) use
@@ -1,6 +1,6 @@
# DPI, bitmaps and fonts
How wx 3.3.2 maps DIP, logical and physical pixels on each platform, how DPI changes reach a window,
How wx 3.3.4 maps DIP, logical and physical pixels on each platform, how DPI changes reach a window,
and how OrcaSlicer sizes layout (`FromDIP`, `em_unit`), rescales (`DPIAware`), rasterizes icons
(`BitmapCache`, `create_scaled_bitmap`, `ScalableBitmap`) and chooses fonts (`Label` table). Read it
for any fixed size, icon, bitmap, image list, font, or `on_dpi_changed` work, and when a bug looks
@@ -488,7 +488,7 @@ handler bound there runs once more after every DPI change.
inherits the DC's scale (`interface/wx/bitmap.h:370-385`).
- `wxBitmap(const wxCursor&)` is invalid on GTK under Wayland (`interface/wx/bitmap.h:388-401`).
- wxMSW `wxBitmap::Create(size, dc)` no longer multiplies by the DC's content scale
(`docs/changes.txt:94-96`).
(`docs/changes.txt:100-102`).
**Offscreen drawing.** "The scaling factor of the bitmap determines the scaling factor used by
this device context" (`interface/wx/dcmemory.h:41-58`); `wxMemoryDC(wxDC*)` does **not** inherit
@@ -740,7 +740,7 @@ are in `references/popups-menus.md`.
in the new code as it doesn't support showing DPI-dependent bitmaps. Please use
wxWithImages::SetImages() instead"; "the size is specified in physical pixels and must correspond
to the size of bitmaps … that will be added". 3.3 made the size physical and makes calls on an
invalid list assert (`docs/changes.txt:53-56, 85-88`) — silently in Orca's assert-free build.
invalid list assert (`docs/changes.txt:59-62, 91-94`) — silently in Orca's assert-free build.
When a list is unavoidable, `wxBitmapBundle::CreateImageList(win, bundles)` builds one at the
consensus size (public but undocumented, [source] `include/wx/bmpbndl.h`).
```cpp
@@ -761,7 +761,7 @@ wxArtProvider::GetBitmapBundle(wxART_WARNING, wxART_OTHER, wxSize(16, 16));
```
**`wxImage`** resizing (`interface/wx/image.h:28-89`): `wxIMAGE_QUALITY_NEAREST` is no longer an
alias of `NORMAL` since 3.3.0 (`docs/changes.txt:98-99`; `wxIMAGE_QUALITY_FAST` is the speed
alias of `NORMAL` since 3.3.0 (`docs/changes.txt:104-105`; `wxIMAGE_QUALITY_FAST` is the speed
synonym). `NORMAL` (default) = bilinear down to an integer multiple, then box average; `HIGH` =
box average when shrinking, bicubic when enlarging; `BILINEAR`, `BICUBIC`, `BOX_AVERAGE` explicit.
High-quality scaling "may not work as expected when using a single mask colour for
@@ -1,6 +1,6 @@
# Events: binding, dispatch, posting and deferred calls
How wx 3.3.2 finds, runs, propagates, queues and drops event handlers, and how OrcaSlicer code
How wx 3.3.4 finds, runs, propagates, queues and drops event handlers, and how OrcaSlicer code
binds, emits and defers. Read it before writing any `Bind`/`Unbind`, `Skip()`, `ProcessEvent`,
`wxPostEvent`/`wxQueueEvent` or `CallAfter`, before defining a custom event, and when debugging a
handler that never runs, runs twice, runs on a dead object, or swallows a widget's own behaviour.
@@ -1,6 +1,6 @@
# Mouse, keyboard and focus
How input reaches wx windows in the wxWidgets 3.3.2 build Orca ships, and the Orca conventions on
How input reaches wx windows in the wxWidgets 3.3.4 build Orca ships, and the Orca conventions on
top: mouse capture, mouse and key events, accelerators and Orca's shortcut registry, focus,
tooltips and cursors. Read it when a widget captures the mouse or tracks hover, when adding or
changing a keyboard shortcut, when touching focus, tooltip or cursor code, and when debugging an
@@ -297,7 +297,7 @@ Per port [source]:
non-enter event while the cached position is invalid is treated as the enter.
- **GTK:** crossing events with a grab/ungrab mode are ignored; outside capture wx re-derives the
window under the pointer on each motion (`src/gtk/window.cpp:2073-2117, 2384-2456`; fixes #24339
and #24931–#24933 are in this release, `docs/changes.txt:537, 547`).
and #24931–#24933 are in this release, `docs/changes.txt:730, 740`).
- **macOS:** each view's `NSTrackingArea` uses `NSTrackingInVisibleRect`
(`src/osx/cocoa/window.mm:3924`) and covers its children, so do not rely on the parent getting
LEAVE when the pointer moves onto a child. `NSMouseMoved` is delivered only to the deepest view
@@ -334,7 +334,7 @@ A composite's hover state must therefore track its children. Orca's `StateHandle
`CS_DBLCLKS`, `src/msw/app.cpp:584`). GTK drops the surplus press before `GDK_2BUTTON_PRESS`
(`src/gtk/window.cpp:1798-1817`), and GTK2 also suppresses triple clicks (`:1818-1829`). On wxOSX the
third press of a triple click is a DOWN again, as on MSW (`src/osx/cocoa/window.mm:4105-4140`;
#25886, `docs/changes.txt:316`). `GetClickCount()` is "implemented only in wxMac and returns -1
#25886, `docs/changes.txt:509`). `GetClickCount()` is "implemented only in wxMac and returns -1
for the other platforms" (`interface/wx/event.h:2965-2974`).
- **Context menu:** "under MSW the context menu event is generated after EVT_RIGHT_UP … but under
GTK … after EVT_RIGHT_DOWN", so a window handling `wxEVT_CONTEXT_MENU` must not handle (or must
@@ -398,7 +398,7 @@ A diagonal trackpad scroll on macOS sends two events, vertical first, then one w
| `ClientToScreen()`, `ScreenToClient()`, `GetScreenRect()` | — | add the top-level window's `gdk_window_get_origin()` (`src/gtk/window.cpp:4630-4698`). Without a global origin the result is only meaningful relative to the same top-level window: fine for deltas and hit tests inside one window, wrong across windows or for absolute placement |
| `wxFindWindowAtPoint(pt)` | deepest window at a screen point; disabled children count, hidden ones are skipped (`interface/wx/utils.h:385-394`) | built on screen rectangles, so unreliable. On GTK it is `wxGenericFindWindowAtPoint`, a walk over every top-level window and child (`src/gtk/utilsgtk.cpp:98-101`, `src/common/utilscmn.cpp:1294-1345`) |
| `wxGetKeyState(key)` | "In wxGTK, this function can be only used with modifier keys … when not using X11 backend" (`interface/wx/utils.h:352-358`) | Ctrl/Alt/Shift and Caps/Num/Scroll Lock only; any other key returns false (`src/unix/utilsx11.cpp:2596-2662`) |
| `WarpPointer()` | Apple's HIG forbids it; on Wayland it works only with a compositor implementing the pointer-warp protocol, and mutter also needs a pressed button (`interface/wx/window.h:3895-3914`; `docs/changes.txt:294`) | — |
| `WarpPointer()` | Apple's HIG forbids it; on Wayland it works only with a compositor implementing the pointer-warp protocol, and mutter also needs a pressed button (`interface/wx/window.h:3895-3914`; `docs/changes.txt:487`) | — |
| `wxUIActionSimulator` | "doesn't work when using Wayland" (`interface/wx/uiaction.h:20`) | — |
| `PopupMenu(x, y)` | — | GTK ≥ 3.22 positions it relative to the window (`gtk_menu_popup_at_rect`, `src/gtk/window.cpp:6520-6565`), so it is safe |
@@ -540,7 +540,7 @@ The full Esc/close path is in `references/windows-dialogs.md`.
layout"; specials are their `WXK_*` (`:1371-1394`). A Cyrillic `ц` gives `'W'`, so Ctrl+letter
shortcuts work across layouts, but an AZERTY key reports its own label (`$` where US has `]`), so
`Ctrl-;`-style punctuation accelerators may be untypeable on some layouts (`:1396-1407`). wxGTK
got the non-Latin mapping in 3.3.0 (#23379, `docs/changes.txt:540`).
got the non-Latin mapping in 3.3.0 (#23379, `docs/changes.txt:733`).
- `wxEVT_CHAR` reflects Shift and the layout. Ctrl+letter gives 1..26 (`WXK_CONTROL_A` …,
`:1409-1421`). Exception: on macOS Cmd+letter's CHAR is the letter itself (`'a'`/`'A'`); only the
physical Control key yields 1..26 (`src/osx/cocoa/window.mm:304-307`).
@@ -629,8 +629,14 @@ Space/Return holds the mouse capture until its KEY_UP reaches the button. On MSW
### Contract
- "An accelerator takes precedence over normal processing" (`interface/wx/accel.h:178`), but it
runs after `wxEVT_CHAR_HOOK` on every port (table above).
- "An accelerator takes precedence over normal processing" (`interface/wx/accel.h:178`). Since 3.3.4 this
holds on every port (wxGTK used to let the focused window handle the key first), unless the focused
window claims the key. The accelerator walk still runs after `wxEVT_CHAR_HOOK` (table above). The new
`wxEVT_ACCELERATOR_KEY` event (`interface/wx/event.h:1740-1801`, `@since 3.3.4`) is sent to the focus
window — and propagated to its parents — before an accelerator is used; it is sent after
`wxEVT_CHAR_HOOK` on most ports and before it on macOS. Its handler can call `UseAccelerator()`, and a
window that must keep the key overrides `wxWindow::ClaimsKeyBeforeAccelerator()`
(`interface/wx/window.h:4554-4583`).
- [source] On GTK and macOS a hit is sent as `wxEVT_MENU` with the entry's id and, if unprocessed,
retried as `wxEVT_BUTTON`; macOS treats the key as consumed either way
(`src/gtk/window.cpp:1340-1366`, `src/osx/window_osx.cpp:2563-2590`). On MSW the `WM_COMMAND` goes to
@@ -654,11 +660,16 @@ Space/Return holds the mouse capture until its KEY_UP reaches the button. On MSW
them in `wxMenuBar::RebuildAccelTable` (`src/msw/menu.cpp:1264-1292`); GTK adds the menu's accel
group to the top-level window in `AttachToFrame` (`src/gtk/menu.cpp:239-247`). A menu shown with
`PopupMenu` treats `"\tCtrl+X"` as display text.
- **MSW:** accelerators are not translated while a `wxTextCtrl`/`wxComboBox`/`wxSpinCtrl` has focus and
the key is a text-editing key: Ctrl+A/C/V/X/Ins/Del/Home/End/Left/Right, Shift+those navigation
keys, bare Del/Home/End, Alt+Backspace (`src/msw/textentry.cpp:1073-1150`). Multi-line controls also
keep Enter (`src/msw/textctrl.cpp:2110-2135`).
- **GTK:** the accelerator walk has no such exemption (`src/gtk/window.cpp:1340-1366`). As menu
- **Text-entry controls claim their editing keys on every port since 3.3.4.** A `wxTextCtrl`, a
non-read-only `wxComboBox` or a `wxSpinCtrl` overrides `ClaimsKeyBeforeAccelerator()` to return
`wxTextEntryBase::IsUsedForEditing()` (`src/common/textcmn.cpp:136-140`;
`src/common/spinctrlcmn.cpp:160-167`; `src/msw/combobox.cpp:389-395`, `src/gtk/combobox.cpp:205-211`):
Alt+Backspace; any printable key with no modifier or Shift (except Delete, so Esc/Enter/Tab stay
usable); Backspace/Delete/Home/End with no modifier or Shift; Ctrl+A/C/V/X; and Insert/Delete/Home/
End/Left/Right with Ctrl or Shift. A matching menu accelerator does not fire while such a control has
focus. The set lives in `IsUsedForEditing` (`src/common/textentrycmn.cpp:441-509`).
- **GTK:** the accelerator walk now consults `ClaimsKeyBeforeAccelerator()` too
(`src/gtk/window.cpp:1524-1539`), so the text-control exemption above applies here since 3.3.4. As menu
accelerators, Shift with non-alphabetic keys does not work, bare arrow keys do not work, and the
listed keys (Tab, the modifiers, locks …) are unsupported (`interface/wx/menuitem.h:562-575`).
- **macOS:** menu-bar items become `NSMenuItem` key equivalents
@@ -69,9 +69,12 @@ Contents: [The stack](#the-stack-orcas-gui-is-built-on) ·
## The stack Orca's GUI is built on
- **wxWidgets 3.3.2, SoftFever fork.** `deps/wxWidgets/wxWidgets.cmake` fetches
`https://github.com/SoftFever/Orca-deps-wxWidgets` at tag `v3.3.2` and builds it static
(`-DwxBUILD_SHARED=OFF`); Flatpak builds build it shared. Linux builds against **GTK3** by default
- **wxWidgets 3.3.4, upstream.** `deps/wxWidgets/wxWidgets.cmake` fetches
`https://github.com/wxWidgets/wxWidgets` at tag `v3.3.4` and builds it static
(`-DwxBUILD_SHARED=OFF`); Flatpak builds build it shared. Two in-tree platform patches apply on
Apple and Windows only (`0001-macos-use-srgb-colour-components.patch`,
`0002-clang-cl-sibling-compiler-prefix.patch`) — `references/platforms.md`. Linux builds against
**GTK3** by default
(`option(DEP_WX_GTK3 "Build wxWidgets against GTK3" ON)` in `deps/CMakeLists.txt`, `SLIC3R_GTK`
default `"3"`, Flatpak uses gtk3). GTK2 exists only as an opt-out (`-DDEP_WX_GTK3=OFF`) and loses
EGL, WebKit2 and DIP pixels. Code guarded for GTK should still compile on GTK2, but GTK3 under X11
@@ -85,11 +88,12 @@ Contents: [The stack](#the-stack-orcas-gui-is-built-on) ·
- **Orca's own widget library.** New UI code largely does not use raw wx controls: the owner-drawn
widgets in `src/slic3r/GUI/Widgets/` (`Button`, `CheckBox`, `ComboBox`, `TextInput`, `SpinInput`,
`SwitchButton`, `RadioGroup`, `Label`, `DialogButtons`, …) replace them. Reasons: native controls
cannot follow Orca's look or its app-level dark-mode toggle; on Windows wx's native dark mode does
not reach anything built on `TaskDialog()` (`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`,
`wxProgressDialog`) nor the wrapped common dialogs (`wxColourDialog`, `wxFontDialog`, …)
(`interface/wx/app.h:1434-1443`), so Orca shows the `MsgDialog` family and its own generic
`Widgets/ProgressDialog` instead; and on GTK the theme's borders bleed through wrapped native
cannot follow Orca's look or its app-level dark-mode toggle; on Windows wx's native dark mode reaches
the `TaskDialog()`-based boxes (`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`,
`wxProgressDialog`) since 3.3.3 and the wrapped common dialogs (`wxColourDialog`, `wxFontDialog`, …)
since 3.3.4, but the date/time/calendar controls, `wxPrintDialog` on Windows 10 and MDI applications
stay light (`interface/wx/app.h:1442-1448`), so Orca shows the `MsgDialog` family and its own generic
`Widgets/ProgressDialog` for a consistent look; and on GTK the theme's borders bleed through wrapped native
controls (the widgets call `RemoveButtonBorder` / `RemoveInputBorder` under `__WXGTK__`).
Plain containers stay raw (`wxPanel`, `wxBoxSizer`, `wxScrolledWindow`).
- **Namespaces.** Most widgets are in the global namespace; a few (`DialogButtons`, `HyperLink`,
@@ -67,9 +67,10 @@ Contents: [Rules](#rules) · [Why the library exists](#why-the-library-exists)
Native controls cannot carry Orca's flat, rounded, palette-coloured look, and they cannot follow all of Orca's
theming:
- On Windows the theme is an app-level setting (Preferences "Enable dark Mode", Windows-only) layered on
`MSWEnableDarkMode`, and wx's MSW dark mode does not reach `TaskDialog()`-based dialogs (`wxMessageBox`,
`wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`), the common dialogs or the date/time pickers
(`interface/wx/app.h:1436-1445`). Hence the `MsgDialog` family (`references/windows-dialogs.md`) and Orca's own
`MSWEnableDarkMode`; wx's MSW dark mode now reaches the `TaskDialog()`-based dialogs (`wxMessageBox`,
`wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`) since 3.3.3 and the common dialogs since 3.3.4,
but the date/time pickers, `wxPrintDialog` on Windows 10 and MDI applications stay light
(`interface/wx/app.h:1442-1448`). Hence the `MsgDialog` family (`references/windows-dialogs.md`) and Orca's own
`ProgressDialog`.
- On macOS and Linux Orca follows the system appearance; native controls are themed by the toolkit, but any
light palette colour set on them still needs the `UpdateDarkUI` pass (`references/colours-dark-mode.md`).
@@ -6,7 +6,7 @@ to write a custom control on the wx side and how to author an Orca widget on the
`StaticBox`/`StateHandler` foundation. Read it before writing or reviewing any `wxEVT_PAINT`
handler, `render`/`doRender` method, `messureSize`, or a new class under `src/slic3r/GUI/Widgets/`.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth).
wx cites are relative to the pinned wx 3.3.4 tree (located as in `SKILL.md` §Ground truth).
Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every
wx assert below is compiled out and `wxCHECK*` returns silently, so paint misuse shows up only as
wrong, missing or stale pixels, never as an assert dialog.
@@ -116,7 +116,7 @@ Bind(wxEVT_PAINT, [this](wxPaintEvent&) {
| Situation | MSW | macOS | GTK3 (X11 and Wayland) | GTK2 (opt-out build) |
|---|---|---|---|---|
| Handler creates no `wxPaintDC` | `wxWindowMSW::HandlePaint` sees no DC and lets `DefWindowProc` validate the region: no `WM_PAINT` storm in 3.3.2, but drawing is unclipped and the contract is broken (`src/msw/window.cpp` `HandlePaint`) | no error; the window shows only what the background step left (undetermined under `wxBG_STYLE_PAINT`) | same | same |
| Handler creates no `wxPaintDC` | `wxWindowMSW::HandlePaint` sees no DC and lets `DefWindowProc` validate the region: no `WM_PAINT` storm in 3.3.4, but drawing is unclipped and the contract is broken (`src/msw/window.cpp` `HandlePaint`) | no error; the window shows only what the background step left (undetermined under `wxBG_STYLE_PAINT`) | same | same |
| Handler creates a DC, then `Skip()`s | default `OnPaint` (native painting) runs as well | — | — | — |
| `wxPaintDC` outside a paint event | `wxCHECK_RET(!paintStack.empty(), "wxPaintDC can't be created outside wxEVT_PAINT handler")` (also for a window other than the one being repainted): the DC gets no HDC and every draw call is a silent no-op (`src/msw/dcclient.cpp` `wxPaintDCImpl::wxPaintDCImpl`) | no visible effect | `wxCHECK_RET(cr, "using wxPaintDC without being in a native paint event")`, every draw call is a silent no-op (`src/gtk/dc.cpp:518`) | works (GDK) |
| `wxClientDC` drawing | works on non-composited windows; the doc lists "wxMSW when using double buffering" (`WS_EX_COMPOSITED`, see below) as non-working (`interface/wx/dcclient.h:82-84`), yet `CanBeUsedForDrawing` returns `true` unconditionally (`include/wx/msw/dcclient.h:60`), so it cannot detect that case | never; `CanBeUsedForDrawing` is `false` (`include/wx/osx/dcclient.h:58`) | X11 works; Wayland has no effect, `CanBeUsedForDrawing` is `false` on `wxDisplayWayland` (`src/gtk/dc.cpp` `wxClientDCImpl::CanBeUsedForDrawing`) | works |
@@ -283,8 +283,8 @@ void MyCard::OnPaint(wxPaintEvent&) {
ancestor up to the TLW (`src/msw/window.cpp` `wxWindowMSW::IsDoubleBuffered`);
`SetDoubleBuffered(on)` toggles that style; `MSWDisableComposited()` removes it up the chain.
`WS_EX_COMPOSITED` makes the whole subtree paint bottom-up into one buffer.
- wx 3.3.0 enabled double buffering for all MSW windows (#22851, `docs/changes.txt:557`) and 3.3.2
reverted it (#25808, `docs/changes.txt:308`). In 3.3.2 nothing in wx sets `WS_EX_COMPOSITED`
- wx 3.3.0 enabled double buffering for all MSW windows (#22851, `docs/changes.txt:750`) and 3.3.2
reverted it (#25808, `docs/changes.txt:501`). In 3.3.4 nothing in wx sets `WS_EX_COMPOSITED`
unless the app calls `SetDoubleBuffered(true)`: MSW windows are not double-buffered by default,
exactly as in 3.2.
- macOS: always double-buffered, `IsDoubleBuffered()` returns `true` (`include/wx/osx/window.h:161`);
@@ -439,7 +439,7 @@ Two variants exist:
(`src/msw/gdiimage.cpp` `wxGDIImage::GetScaleFactor`, `src/msw/dcmemory.cpp`; details in
`references/dpi-bitmaps-fonts.md §wxBitmap`), while `GetLogicalSize()` stays the physical size there.
- MSW `wxBitmap::Create(size, dc)` no longer multiplies by the DC content scale; the size is
physical (`docs/changes.txt:94-96`).
physical (`docs/changes.txt:100-102`).
**Usage — portable back buffer.**
```cpp
@@ -556,7 +556,7 @@ already prepared ([Buffered painting](#buffered-painting-and-double-buffering)).
must set the appropriate fonts and colours" (`interface/wx/dc.h:775-780`). `GetFont()` returns `wxNullFont` until
`SetFont()` is called (`interface/wx/dc.h:715-720`).
- wx 3.3 moved the non-drawing members to `wxReadOnlyDC` and several overridable measuring virtuals
now take it (`docs/changes.txt:228-231`); see `references/wx-33-changes.md`.
now take it (`docs/changes.txt:260-263`); see `references/wx-33-changes.md`.
**OrcaSlicer.** Orca widgets measure in `messureSize()`. A `messureSize()` that builds a
`wxClientDC dc(this)` (`Button::messureSize`, `TextInput::messureSize`) measures correctly on every
@@ -1,7 +1,7 @@
# Platforms, the wx build, and platform-specific code
Read this when code has to differ per platform, when a bug shows up on only one OS, toolkit or display
server, or when you need to know how Orca's wxWidgets is built. It covers the wx fork and its build
server, or when you need to know how Orca's wxWidgets is built. It covers the wx source pin and its build
options, platform macros, per-platform summaries that point into the topic files, and where
platform-specific code lives. It also owns runtime X11/Wayland detection, the Wayland gap list, custom
title bars and window decoration, GTK native-chrome removal, and the cross-platform test checklist.
@@ -58,37 +58,46 @@ Contents: [Rules](#rules) · [The wx build Orca uses](#the-wx-build-orca-uses)
## The wx build Orca uses
### Source, pin and local patch
### Source, pin and local patches
- `deps/wxWidgets/wxWidgets.cmake` builds `https://github.com/SoftFever/Orca-deps-wxWidgets` at tag
**`v3.3.2`** (`GIT_SHALLOW ON`, submodules `3rdparty/catch`, `3rdparty/pcre` and `3rdparty/libwebp`
only). The fork carries Orca's build fixes, the clang-cl fix among them; do not duplicate
a fork fix as a local patch under `deps/wxWidgets/` (cc390f11ee removed the local
`0001-Clang-CL-fix.patch` once the fork carried the fix). The fork's clang-cl fix is the MSVC lib-dir
selection in the installed `wxWidgetsConfig.cmake`: it looks for `<prefix>_<arch>_lib` (or `_dll`)
under the consuming compiler's prefix first and then the sibling one (`clang` ↔ `vc`), because cl
and clang-cl share an ABI and either can consume either build
(`build/cmake/wxWidgetsConfig.cmake.in:53-73`).
- **The one local patch** is `deps/wxWidgets/0001-macos-use-srgb-colour-components.patch`, applied
- `deps/wxWidgets/wxWidgets.cmake` builds upstream `https://github.com/wxWidgets/wxWidgets` at tag
**`v3.3.4`** (`GIT_SHALLOW ON`, submodules `3rdparty/catch`, `3rdparty/pcre` and `3rdparty/libwebp`
only). Two in-tree patches under `deps/wxWidgets/` are applied, one per platform, so neither applies
on Linux: `0001-macos-use-srgb-colour-components.patch` on Apple and
`0002-clang-cl-sibling-compiler-prefix.patch` on Windows. The clang-cl patch is the old fork's
clang-cl fix ported back to a local patch: it changes the MSVC lib-dir selection in the installed
`wxWidgetsConfig.cmake` so it looks for `<prefix>_<arch>_lib` (or `_dll`) under the consuming
compiler's prefix first and then the sibling one (`clang` ↔ `vc`), because cl and clang-cl share an
ABI and either can consume either build (`build/cmake/wxWidgetsConfig.cmake.in:53-73`). Because the
pin is upstream, every other build fix must live as a local patch under `deps/wxWidgets/`.
- **The Apple patch** is `deps/wxWidgets/0001-macos-use-srgb-colour-components.patch`, applied
only `if (APPLE)`. The `PATCH_COMMAND` first runs `git checkout -f -- src/osx/cocoa/colour.mm` and
then `git apply`, so the step can run again safely (a7775296b0). The patch makes the wxOSX
`wxColour` component getters (`wxNSColorRefData::Red/Green/Blue/Alpha` and `IsSolid`) convert the
`NSColor` with `[NSColorSpace sRGBColorSpace]` instead of `NSCalibratedRGBColorSpace`, so colours
read back on macOS match their sRGB values (custom-colour accuracy). Colour usage:
`references/colours-dark-mode.md`.
- **The Windows patch** is `deps/wxWidgets/0002-clang-cl-sibling-compiler-prefix.patch`, applied only
`if (WIN32)`. Its `PATCH_COMMAND` first runs
`git checkout -f -- build/cmake/wxWidgetsConfig.cmake.in` and then `git apply`, so the step can run
again safely. It rewrites the `WIN32_MSVC_NAMING` branch of `build/cmake/wxWidgetsConfig.cmake.in`
to try the consuming compiler's prefix first and then the sibling one (`clang` ↔ `vc`) when locating
the installed `<prefix>_<arch>_lib`/`_dll` directory.
- The checked-out source is the tree that every wx citation in this skill refers to:
`deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets` on macOS and
`deps/<tree>/dep_wxWidgets-prefix/src/dep_wxWidgets` on Linux and Windows (`deps/build` for a
release build; `build_win.bat` names the others). Locate it with the bash or PowerShell lookup in
`SKILL.md` §Ground truth. On macOS its
`src/osx/cocoa/colour.mm` already has the patch applied.
`src/osx/cocoa/colour.mm` already has the Apple patch applied; on Windows its
`build/cmake/wxWidgetsConfig.cmake.in` already has the clang-cl patch applied.
- **Flatpak builds wx separately.** `deps/CMakeLists.txt` leaves `dep_wxWidgets` out of the deps
target when `FLATPAK` is set. Instead, `scripts/flatpak/com.orcaslicer.OrcaSlicer.yml` has its own
`wxWidgets` module whose config-opts "mirror deps/wxWidgets/wxWidgets.cmake with FLATPAK=ON,
DEP_WX_GTK3=ON": `-DwxBUILD_TOOLKIT=gtk3`, a shared build (`wxBUILD_SHARED=ON`,
`BUILD_SHARED_LIBS=ON`, `d` debug postfix), and `wxUSE_LIBWEBP=sys`, because the builtin webp
libraries are installed only by static builds. It links with lld and pins the fork's tag
`orca-3.3.2` at a fixed commit. Option and version changes must be made in both files.
libraries are installed only by static builds. It links with lld and pins upstream `v3.3.4` at
commit `6a42a4b999b127cf28a8b5fe686da6b8ca93a9b8`. Option and version changes must be made in both
files; it applies neither patch (GTK3, no MSVC).
### Toolkit per platform
@@ -188,7 +197,7 @@ Its `wx/gtk/private*` includes are commented out. The per-port headers outside t
`wx/msw/private.h` (`BitmapComboBox.cpp`, `PresetComboBoxes.cpp`, Windows-guarded) and
`wx/osx/private.h` (`Utils/MacDarkMode.mm`), come with the regular wx install. Private headers are
port-specific and unversioned. Include one only under the same macro wx uses for that port or
feature, and re-check it whenever the fork is bumped.
feature, and re-check it whenever the wx pin is bumped.
### wxInspector
@@ -268,8 +277,8 @@ Each bullet names the mechanism and the file that owns it.
popups with it are dismissed on deactivation, deferred through `CallAfter`. No key dismisses a
popup, and `ProcessLeftDown` is never called. `PopupWindow::BindUnfocusEvent` is MSW-only →
`references/popups-menus.md` §5.
- **Painting:** in 3.3.2 windows are not double-buffered by default (the 3.3.0 global
`WS_EX_COMPOSITED` was reverted, `docs/changes.txt:308`). Custom widgets buffer by hand →
- **Painting:** in 3.3.4 windows are not double-buffered by default (the 3.3.0 global
`WS_EX_COMPOSITED` was reverted, `docs/changes.txt:501`). Custom widgets buffer by hand →
`references/painting-custom-widgets.md`.
- **Building large panels:** every control is a native child window, and outside a sizer pass a move
or resize is immediate, repainting when the window is shown [source: `src/msw/window.cpp:2036`
@@ -284,8 +293,9 @@ Each bullet names the mechanism and the file that owns it.
- **Mouse capture:** `wxEVT_MOUSE_CAPTURE_LOST` and `wxEVT_MOUSE_CAPTURE_CHANGED` are delivered →
`references/mouse-keyboard-focus.md`.
- **Controls:** `wxDataViewCtrl` is the generic implementation (`references/controls-dataview.md`).
TaskDialog-based dialogs and common dialogs stay light in wx dark mode
(`interface/wx/app.h:1434-1448`); Orca's `MsgDialog` family is owner-drawn →
TaskDialog-based dialogs (since 3.3.3) and common dialogs (since 3.3.4) follow wx dark mode; the
date/time/calendar controls, `wxPrintDialog` on Windows 10 and MDI applications stay light
(`interface/wx/app.h:1442-1448`); Orca's `MsgDialog` family is owner-drawn →
`references/windows-dialogs.md`.
- **WebView:** Edge (WebView2). It needs the runtime (checked by `GUI_App::init_webview_runtime`),
creates asynchronously, serves custom schemes as `https://<scheme>.wxsite`, and allows one script
@@ -616,16 +626,16 @@ headers. Rows marked [source] or "protocol" come from the implementation or from
| Area | What happens on Wayland | Cite | Orca handling / owner |
|---|---|---|---|
| Global pointer position | `wxGetMousePosition()` / `wxGetMouseState()` call `gdk_device_get_position` [source], but Wayland gives clients no global pointer position, so the result is not a screen position | `src/gtk/window.cpp` `wxGetMousePosition` | use event coordinates + `ClientToScreen` (`BBLTopbar`); dismiss popups by focus tracking (`SearchDialog::Dismiss`) → `references/mouse-keyboard-focus.md` |
| Top-level position | the compositor places windows, and `SetPosition()` / `Move()` on a TLW have no effect (protocol) | — | `GUI_App::window_pos_restore` restores only size and maximised state; moves go through `gtk_window_begin_move_drag` |
| Top-level position | the compositor places windows, and `SetPosition()` / `Move()` on a TLW have no effect (protocol). 3.3.4 added geometry restore through the Wayland XDG session-management protocol (`SaveGeometry`/`RestoreToGeometry` with the new `SaveString`/`RestoreString` fields) | `interface/wx/toplevel.h:439-455, 474-490` | `GUI_App::window_pos_restore` restores only size and maximised state; moves go through `gtk_window_begin_move_drag` |
| Window icon | `SetIcon()` / `SetIcons()` "doesn't do anything when using Wayland … create a `.desktop` file" | `interface/wx/toplevel.h:517-521, 538-542` | `src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop` (`Icon=OrcaSlicer`, `StartupWMClass=orca-slicer`) |
| App id | `wxAppConsole::SetClassName()` is the xdg `app_id` with GTK ≥ 3.24.22 (and the AUMID on Windows); it must be set before any TLW. wx applies it when a TLW is mapped, and only if it is non-empty [source: `wxTopLevelWindowGTK::GTKHandleMapped`] | `interface/wx/app.h:765-812` | Orca calls only `SetAppName(SLIC3R_APP_KEY)`, so GTK's default applies. On Windows, `SetClassName` would also change shell behaviour (MRU, Shift+middle-click) |
| `wxClientDC` | deprecated in 3.3 ("please use wxInfoDC instead for obtaining information", `interface/wx/dcclient.h:43-46`). Drawing through it "simply doesn't have any effect" on GTK3/Wayland or wxOSX. `CanBeUsedForDrawing()` [source]: false on Wayland only for wxGTK (`src/gtk/dc.cpp`), always false on wxOSX, always true on wxMSW (`include/wx/{osx,msw}/dcclient.h`), although its doc also lists "wxMSW when using double buffering" | `interface/wx/dcclient.h:48-53, 80-88` | draw only in `wxPaintDC` after `Refresh()`/`RefreshRect()` → `references/painting-custom-widgets.md` |
| `wxWindow::Update()` | "doesn't do anything in wxGTK port when using Wayland". [source] wx skips the GDK update calls there because they broke later updates (#25036) | `interface/wx/window.h:2405-2407` | never rely on `Update()` to paint synchronously |
| `WarpPointer()` | works only if the compositor implements the pointer-warp protocol; mutter also needs a mouse button held | `interface/wx/window.h:3902-3907`; `docs/changes.txt:294` | Orca never warps the pointer |
| `WarpPointer()` | works only if the compositor implements the pointer-warp protocol; mutter also needs a mouse button held | `interface/wx/window.h:3902-3907`; `docs/changes.txt:487` | Orca never warps the pointer |
| `wxUIActionSimulator` | "currently doesn't work when using Wayland with wxGTK" | `interface/wx/uiaction.h:20` | not used; Orca also builds `wxUSE_XTEST=OFF` |
| `wxBitmap(const wxCursor&)` | creates an invalid bitmap | `interface/wx/bitmap.h:393-395` | — |
| OpenGL | only EGL; `PreferGLX()` has no effect. Without EGL in the build, wxGTK's `wxGLCanvas` shows a fatal message and refuses to work [source: `src/gtk/glcanvas.cpp` `IsAvailable`]. The EGL surface is a subsurface over the canvas, ready only after map and a frame callback [source: `src/unix/glegl.cpp`] | `interface/wx/glcanvas.h:1094-1095` | `CLI::run` forces X11 when wx lacks EGL; overlays are drawn in GL/ImGui, never as wx children over the canvas → `references/webview-gl-aui-media.md` |
| AUI | the doc note "live resize is always used … for wxOSX and wxGTK3 when using Wayland" is obsolete: "As of wxWidgets 3.3.0 this function always returns false", and `wxAUI_MGR_LIVE_RESIZE` is in the default flags. Floating panes need global positions | `interface/wx/aui/framemanager.h:336-345` | `Plater::priv::priv` clears `wxAUI_MGR_ALLOW_FLOATING`; `sanitize_window_layout_for_wayland` strips floating state from the saved layout → `references/webview-gl-aui-media.md` §wxAuiManager |
| AUI | the doc note "live resize is always used … for wxOSX and wxGTK3 when using Wayland" is obsolete: "As of wxWidgets 3.3.0 this function always returns false", and `wxAUI_MGR_LIVE_RESIZE` is in the default flags. AUI's docking-hint logic still needs global positions; 3.3.4 added system drag sessions (`StartDragSession`) so wx can move a pane's floating frame under Wayland anyway | `interface/wx/aui/framemanager.h:336-345`; `src/aui/framemanager.cpp:4574-4599` | `Plater::priv::priv` clears `wxAUI_MGR_ALLOW_FLOATING`; `sanitize_window_layout_for_wayland` strips floating state from the saved layout → `references/webview-gl-aui-media.md` §wxAuiManager |
| Popups | a GTK popup is an `xdg_popup` only for COMBO/DROPDOWN/POPUP_MENU hints, so wx creates popups with `GDK_WINDOW_TYPE_HINT_COMBO` [source]. A chained popup's parent must be the mapped popup, and mapping it with a grab deactivates the toplevel (Orca's comments in `DropDown::mouseMove`, `DropDown::ShouldDismissOnTopWindowDeactivate`) | `src/gtk/popupwin.cpp:110-114` | `DropDown` transient-for chain, `ShouldDismissOnTopWindowDeactivate` → `references/popups-menus.md` |
| Fractional scale | arrives as an integer GDK scale | `docs/doxygen/overviews/high_dpi.md:348-351` | → `references/dpi-bitmaps-fonts.md` |
| Window decorations | some desktop environments draw a title bar on undecorated windows anyway | — | [Undecorated windows](#wayland-undecorated-top-level-windows-splash) |
@@ -636,7 +646,9 @@ were two-finger scrolling (703, 3.1.3), the EGL-based `wxGLCanvas` for Wayland (
support (498, both 3.1.5). New with the upgrade: "Many bug fixes for Wayland-specific problem" (397)
and a `wxMediaCtrl` fix (402) in 3.1.6, GDK errors from `PopupMenu()` avoided (317, 3.1.7),
`wxCURSOR_SIZING` fixed (261, 3.2.0). In 3.3, `WarpPointer()` on supported compositors
(`docs/changes.txt:294`) and the EGL/Wayland high-DPI scale fix (`changes.txt:538`).
(`docs/changes.txt:487`) and the EGL/Wayland high-DPI scale fix (`changes.txt:731`); in 3.3.4, a fix for
the crash on pointer/seat disconnect (`changes.txt:371`), AUI pane dragging via system drag sessions
(`:372`) and window-geometry restore via XDG session management (`:373`).
## Window decoration and custom title bars
@@ -658,7 +670,7 @@ There is no `wxCAPTION` on any platform; each port then needs its own handling.
**Contract** [source + documented]. Since wx **3.3.0**, `wxTopLevelWindowMSW::MSWGetStyle` adds
`WS_CAPTION` whenever any of `wxCAPTION | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX` is set
(`src/msw/toplevel.cpp:133-135`). The 3.3.0 wxMSW change list says "Turn wxCAPTION on automatically if
required by other styles (#23575)" (`docs/changes.txt:581`). Commit eefdabcd98 attributes this to
required by other styles (#23575)" (`docs/changes.txt:774`). Commit eefdabcd98 attributes this to
3.3.2, but it is a 3.3.0 change. `SetWindowStyleFlag()` recomputes the native style through
`MSWGetStyle` and turns the bits back on (`src/msw/window.cpp` `wxWindowMSW::MSWUpdateStyle`).
@@ -868,10 +880,11 @@ do both). Cite: 6148ba16b3, 988b500f33.
## wx 3.3 migration notes
Orca moved from **3.1.5** to 3.3.2 in 8248b06337 ("Updated wxWidgets to 3.3.2", #12941; build system
in 2d7e26292b), so the 3.1.6–3.2.0 incompatible changes (`docs/changes_32.txt`) apply as well as
`docs/changes.txt`. The version digest, every migration commit as a rule for new code, and the
post-upgrade regressions to watch are owned by `references/wx-33-changes.md` (§8). The
Orca moved from **3.1.5** to **3.3.2** in 8248b06337 ("Updated wxWidgets to 3.3.2", #12941; build system
in 2d7e26292b); the pin is now upstream **3.3.4** (`deps/wxWidgets/wxWidgets.cmake`, tag `v3.3.4`), so
the 3.1.6–3.2.0 incompatible changes (`docs/changes_32.txt`) apply as well as `docs/changes.txt`. The
version digest, every migration commit as a rule for new code, and the post-upgrade regressions to
watch are owned by `references/wx-33-changes.md` (§10); the 3.3.3 and 3.3.4 changes are its §8–§9. The
platform-specific ones are described in this file: the `MainFrame` `WS_CAPTION` handling
([MSW title bar](#msw-the-mainframe-custom-title-bar)), the macOS `kAEGetURL` re-registration and the
`SidePopup` anchoring ([ifdef landscape](#the-ifdef-landscape)), and the GTK criticals filter
@@ -1,6 +1,6 @@
# Popups, transient windows and menus
How wx 3.3.2 popups and menus behave on each port, and the Orca wrappers built on them: `PopupWindow`,
How wx 3.3.4 popups and menus behave on each port, and the Orca wrappers built on them: `PopupWindow`,
`DropDown`/`ComboBox`, `SidePopup`, the search popups, frameless dialogs used as popups, `MenuFactory`,
`append_menu_item`, `Plater::PopupMenu` and the macOS menubar versus `BBLTopbar`. Read it before you add
or change anything that opens over other UI and must close by itself, or any context menu or menubar item.
@@ -791,7 +791,7 @@ created with `wxBORDER_NONE | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT | wxF
`wxWindow`. "The window automatically destroys itself when the user clicks on it or it loses the focus"
(`interface/wx/tipwin.h:13-14`). Since 3.3.2 use `static Ref New(parent, text, maxLength, rectBound)`. The
returned `wxTipWindow::Ref` is "guaranteed to become invalid when the tip window is closed", and the raw
constructor is deprecated (`tipwin.h:27-132`; `docs/changes.txt:286`). `Create` positions the tip at the mouse
constructor is deprecated (`tipwin.h:27-132`; `docs/changes.txt:479`). `Create` positions the tip at the mouse
and pops it up immediately (`src/generic/tipwin.cpp:171-240`); on GTK it also captures the mouse.
`SetBoundingRect` closes the tip when the mouse leaves that rectangle (`tipwin.h:160-172`). Orca:
`Button::OnParentMotion` (MSW-only tooltips for disabled buttons) keeps a `wxTipWindow::Ref`.
@@ -800,11 +800,11 @@ created with `wxBORDER_NONE | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT | wxF
`wxRichToolTipPopup` (`src/generic/richtooltipg.cpp`). Re-check it on every wx update.
- **`wxInfoBar`** "calls its parent wxWindow::Layout() method and assumes that it will change the parent
layout": add it to the parent's sizer (`interface/wx/infobar.h:38-44`). It is generic on all ports and native
in wxGTK. Use `ShowMessage(msg, flags)`/`Dismiss()`. 3.3 adds `ShowCheckBox` (`docs/changes.txt:460`).
in wxGTK. Use `ShowMessage(msg, flags)`/`Dismiss()`. 3.3 adds `ShowCheckBox` (`docs/changes.txt:653`).
- **`wxNotificationMessage`** is "not a window", native on MSW, macOS and GTK (`interface/wx/notifmsg.h:12-18`).
MSW recommends `MSWUseToasts()` before the first notification, macOS needs the alert notification style for
actions, and GTK ignores the timeout for warnings and errors (`notifmsg.h:23-38`, 179-182). 3.3 reports the
dismissal reason (`docs/changes.txt:488`). Orca does not use it. Its in-app notifications are the GL-canvas
dismissal reason (`docs/changes.txt:681`). Orca does not use it. Its in-app notifications are the GL-canvas
`NotificationManager`.
## 13. wxMenu and wxMenuItem
@@ -862,7 +862,7 @@ submenu (it leaks), `Destroy(id)` deletes it, and `Remove()` returns ownership (
an item switch it to owner-drawn (`MSWMustUseOwnerDrawn`, `src/msw/menuitem.cpp:1371-1377`). "Owner drawn items
are also incompatible with dark mode support and using them makes the entire menu containing them to use
standard light mode colours even in dark mode." 3.3.2 reworked MSW dark-mode menu rendering
(`docs/changes.txt:305-306`); owner-drawn items still opt out of it.
(`docs/changes.txt:498-499`); owner-drawn items still opt out of it.
**Labels and accelerators** (`menuitem.h:460-490`, 555-570).
- The syntax is `"&Label\tCtrl+X"`; `&&` is a literal ampersand. `CTRL` means Cmd on macOS, and `RAWCTRL` gives
@@ -1,6 +1,6 @@
# Sizers and layout
How wx 3.3.2 computes window sizes and lays out children, and how OrcaSlicer builds layouts on top of
How wx 3.3.4 computes window sizes and lays out children, and how OrcaSlicer builds layouts on top of
that: sizers and flags, best/min size, the fitting functions (`SetSizerAndFit`, `SetSizeHints`, `Fit`,
`Layout`, `FitInside`) per platform, show/hide relayout, Freeze/Thaw, scrolled windows, `wxStaticText`
wrapping, layout on DPI change, and Orca's layout idioms. Read it when building or reviewing any dialog
@@ -604,7 +604,7 @@ Ordinary dialogs and the sidebar use plain `wxScrolledWindow`.
without spaces never wraps with `wxStaticText::Wrap`.
- `wxST_WRAP` (new in 3.3.2): "Wrap label text on multiple lines if necessary, using the available
horizontal space. This style only works when the control is used inside a sizer"
(`interface/wx/stattext.h:46-49`; `docs/changes.txt:280`). It is opt-in; labels without it are
(`interface/wx/stattext.h:46-49`; `docs/changes.txt:473`). It is opt-in; labels without it are
unaffected. It is implemented with `GetMinSizeFromKnownDirection` (`stattextcmn.cpp:285-312`).
**[source]** The initial `CalcMin` still uses the unwrapped best size, so a fitted dialog grows to the
full one-line width and nothing wraps; constrain the width another way (a fixed or min width on the
@@ -44,7 +44,8 @@ Contents: [Rules](#rules) · [Build facts](#build-facts-that-decide-string-behav
12. One complete sentence per msgid, placeholders instead of concatenated fragments, the number
placeholder in both plural forms, `// TRN` for ambiguous strings, a context for homonyms.
→ §Plurals and contexts
13. A wrapper around `wxGetTranslation` returns `wxString` by value. → §Translation
13. A wrapper that converts a translation (`wxString::FromUTF8`, `ToUTF8()`, `format_wxstr`) returns
by value; `wxGetTranslation()` itself returns `const wxString&`. → §Translation
14. User-supplied text (preset, filament, file, printer names) in a mnemonic-interpreting label
goes through `SetLabelText` or `wxControl::EscapeMnemonics`. → §Labels
15. File dialogs: filters from `file_wildcards(FT_*)`, default dir `from_u8(app_config->get_last_dir())`,
@@ -186,11 +187,11 @@ was the splash screen's event filter, fixed in 4088a36095; see `references/threa
**modifies the string**. Use `s[n].GetValue()` or an explicit `int`/`wchar_t` type
(`interface/wx/string.h:171-226`).
- Since 3.3 `wxstr = {"Hello", 2}` is ambiguous (the `string_view` constructor); write
`wxString{"Hello", 2}` (`docs/changes.txt:191-194`).
`wxString{"Hello", 2}` (`docs/changes.txt:223-226`).
- Never pass a `wxString`, `c_str()` or `mb_str()` to a real C vararg function (`printf`); use
`wxString::Format`/`wxPrintf` or convert explicitly (`interface/wx/string.h:262-300`).
- `wxUSE_STL` no longer exists; wx 3.3 re-enables implicit `wxString` → `std::string` only through
`wxUSE_STD_STRING_CONV_IN_WXSTRING=1` (`docs/changes.txt:163-166`). Orca keeps it 0 and adds
`wxUSE_STD_STRING_CONV_IN_WXSTRING=1` (`docs/changes.txt:195-198`). Orca keeps it 0 and adds
`wxNO_UNSAFE_WXSTRING_CONV`; do not enable either conversion, the compile error is the guard.
### Pitfalls
@@ -322,10 +323,13 @@ language's decimal separator; `wxString::ToCDouble`/`FromCDouble` do not.
**wx contract** (`interface/wx/translation.h:577-640`): `wxGetTranslation(string, domain = "",
context = "")` returns the original string when no catalog has it; a non-empty context needs a
matching `msgctxt` in the catalog; the plural overload returns `string` for `n == 1` and `plural`
otherwise when no catalog is found; "This function is thread-safe". Since 3.3 it returns
**`wxString` by value**, not a const reference: "please change the return type of the function to
wxString" (`docs/changes.txt:139-142`). Orca's `I18N::translate` overloads already return by value;
keep any new wrapper that way, a `const wxString&` return now dangles.
otherwise when no catalog is found; "This function is thread-safe". It returns **`const wxString&`**
(`interface/wx/translation.h:615`; `include/wx/translation.h:277-309`). 3.3.2 changed this to a
by-value `wxString` and **3.3.3 reverted it** to the 3.2.x reference, so the reference points at the
catalog's own string or at `wxTranslations::GetUntranslatedString()`, both of which outlive the call.
Forwarding that reference is therefore safe; what must return by value is a wrapper that constructs a
temporary — `wxString::FromUTF8`, `ToUTF8()`, `format_wxstr`. Orca's `I18N::translate` overloads
return `wxString` by value and `translate_utf8`/`_u8L` return `std::string`.
**OrcaSlicer macros** (`src/slic3r/GUI/I18N.hpp`):
@@ -429,7 +433,7 @@ using this class doesn't affect the native controls and dialogs", and on macOS 1
C locale can break the menus (`interface/wx/intl.h:282-292`): native file dialogs, the app menu and
standard buttons follow the system language. `wxLocale::IsAvailable` builds a region tag and asks
`wxUILocale(...).IsSupported()` (`src/common/intl.cpp` `wxLocale::IsAvailable`), which on Unix no
longer falls back to another region of the same language (`docs/changes.txt:75-79`); keep the
longer falls back to another region of the same language (`docs/changes.txt:81-85`); keep the
fallbacks in `load_language`.
- **Rule:** translate at use; never store a translated string in a namespace-scope or function-local
@@ -457,7 +461,7 @@ call `SetLabel(label)`) [source]. `SetLabelMarkup` also treats an
unescaped `&` as a mnemonic, needs `&amp;`/`&lt;` for literal characters, strips the markup where it
is unsupported, and leaves the label unchanged (returns false) when the string is not well-formed
(`interface/wx/control.h:200-360`); user text inside markup must be XML-escaped.
Since 3.3 wxListbook/wxChoicebook also interpret mnemonics in page titles (`docs/changes.txt:128-130`);
Since 3.3 wxListbook/wxChoicebook also interpret mnemonics in page titles (`docs/changes.txt:134-136`);
Orca uses neither, but the rule is the same for every book control.
**OrcaSlicer.** `Label` (`Widgets/Label.cpp`) derives from `wxStaticText`; `Label::SetLabel` stores
@@ -495,7 +499,7 @@ so build it from the wide form (`into_path(w).wstring()`) or stay with boost. At
expands `$VAR`/`%VAR%` inside file names (`interface/wx/filename.h:90-103`); use `MakeAbsolute()` or
explicit `wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE`. 3.3 adds `IsMSWExtendedLengthPath()` for `\\?\`
paths "avoiding the 260 character path length restriction" (`interface/wx/filename.h:1040-1051`,
`docs/changes.txt:551`). `wxFileSystem::FileNameToURL`/`URLToFileName` convert to and from `file:`
`docs/changes.txt:744`). `wxFileSystem::FileNameToURL`/`URLToFileName` convert to and from `file:`
URLs (`interface/wx/filesys.h:96-103, 175-180`).
**`wxStandardPaths`** (`interface/wx/stdpaths.h`): the directories "may or may not exist"
@@ -552,9 +556,9 @@ for (const wxString& p : paths)
| | Behaviour |
|---|---|
| MSW | New-style `IFileDialog`: wx calls `SetDefaultExtension` with the selected filter's first extension and no longer calls `AppendExtension` itself (`src/msw/filedlg.cpp:1644-1656, 1738-1740`; `docs/changes.txt:556`). `SetExtraControlCreator` → XP-style dialog. |
| MSW | New-style `IFileDialog`: wx calls `SetDefaultExtension` with the selected filter's first extension and no longer calls `AppendExtension` itself (`src/msw/filedlg.cpp:1644-1656, 1738-1740`; `docs/changes.txt:749`). `SetExtraControlCreator` → XP-style dialog. |
| macOS | Open dialogs show **no filter choice** and apply all wildcards at once unless `wxSystemOptions::SetOption(wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES, 1)`, and even then non-matching files are only greyed (`interface/wx/filedlg.h:117-128`). Matching compares the lower-cased last path extension (`wxOpenSavePanelDelegate panel:shouldEnableURL:`, `src/osx/cocoa/filedlg.mm:58-88`) [source]: case-insensitive, and a multi-dot pattern such as `*.gcode.3mf` or `*.zip.amf` never matches by itself. `wxFD_OVERWRITE_PROMPT` is always on (`:172-175`); `wxFD_OPEN` always behaves as `wxFD_FILE_MUST_EXIST` (`:184-189`). The save panel replaces the initial file name's extension with the first one in the wildcard (Orca's comment on `file_wildcards`). The native panel runs `runModal` and does not re-raise the parent dialog afterwards (`src/osx/cocoa/filedlg.mm` `ShowModal`); see `references/windows-dialogs.md` for the deferred re-raise. |
| GTK3 | `GtkFileChooserNative` (portal-capable, e.g. Flatpak) when GTK ≥ 3.20 at runtime and neither `wxFD_PREVIEW` nor an extra control/customize hook is used (`src/gtk/filedlg.cpp:265-274, 437-443`; `src/gtk/dirdlg.cpp:122-130`; `docs/changes.txt:393`). It runs through `gtk_native_dialog_run`: there is no wx window, so size/position calls do nothing (`wxFileDialog::DoSetSize` is empty). Patterns go to `gtk_file_filter_add_pattern` per token and are **case-sensitive** (`src/gtk/filectrl.cpp:169`). The chooser is transient for the parent left after `GetParentForModalDialog`, which replaces a null (or hidden, dying or `wxWS_EX_TRANSIENT`) parent with the active top-level window, else the app's main top window (`src/gtk/filedlg.cpp:206, 223-225`; `src/common/dlgcmn.cpp:180-203` `DoGetParentForDialog`) [source]. `wxFD_PREVIEW` is GTK-only (`interface/wx/filedlg.h:195-197`). |
| GTK3 | `GtkFileChooserNative` (portal-capable, e.g. Flatpak) when GTK ≥ 3.20 at runtime and neither `wxFD_PREVIEW` nor an extra control/customize hook is used (`src/gtk/filedlg.cpp:265-274, 437-443`; `src/gtk/dirdlg.cpp:122-130`; `docs/changes.txt:586`). It runs through `gtk_native_dialog_run`: there is no wx window, so size/position calls do nothing (`wxFileDialog::DoSetSize` is empty). Patterns go to `gtk_file_filter_add_pattern` per token and are **case-sensitive** (`src/gtk/filectrl.cpp:169`). The chooser is transient for the parent left after `GetParentForModalDialog`, which replaces a null (or hidden, dying or `wxWS_EX_TRANSIENT`) parent with the active top-level window, else the app's main top window (`src/gtk/filedlg.cpp:206, 223-225`; `src/common/dlgcmn.cpp:180-203` `DoGetParentForDialog`) [source]. `wxFD_PREVIEW` is GTK-only (`interface/wx/filedlg.h:195-197`). |
| GTK2 (opt-out build) | never uses the native chooser; patterns case-sensitive. |
**OrcaSlicer:**
@@ -650,7 +654,7 @@ dtor, and `!lock` tests `IsOpened()`.
| | Behaviour |
|---|---|
| MSW | `SetData`/`GetData` do not check `Open()` (`src/msw/clipbrd.cpp` `wxClipboard::SetData`), so code that forgets it works here only. `wxTextDataObject::SetData()` size must now include the 2-byte NUL (`docs/changes.txt:62-66`); use `SetText()` or the ctor. |
| MSW | `SetData`/`GetData` do not check `Open()` (`src/msw/clipbrd.cpp` `wxClipboard::SetData`), so code that forgets it works here only. `wxTextDataObject::SetData()` size must now include the 2-byte NUL (`docs/changes.txt:68-72`); use `SetText()` or the ctor. |
| macOS | without `Open()`, `SetData`/`AddData`/`GetData` return false through `wxCHECK_MSG(m_open, …)` without taking the object (`src/osx/carbon/clipbrd.cpp:84-105, 140-147`); a successful write is flushed to the pasteboard immediately (`:110-114`). |
| GTK | same `wxCHECK_MSG(m_open, …)` early return (`src/gtk/clipbrd.cpp:643-647, 775`). `GetData`/`IsSupported` are asynchronous underneath: `wxClipboardSync` spins `YieldFor(wxEVT_CATEGORY_CLIPBOARD)` until GTK answers and forbids re-entrancy (`src/gtk/clipbrd.cpp:68-92`). PRIMARY selection exists. Under Wayland, Wayland MIME types are advertised next to the X11 atoms (`src/gtk/clipbrd.cpp:655-700`). |
@@ -793,8 +797,8 @@ from a non-main thread on wxOSX (`:39-40`); `wxCANCEL_DEFAULT` is ignored on wxO
`wxID_OK/wxID_CANCEL/wxID_YES/wxID_NO/wxID_HELP` (`:269-275`), but **`wxMessageBox()` returns
`wxYES/wxNO/wxCANCEL/wxOK/wxHELP`** (`:308-312`). `wxRichMessageDialog` is native only on MSW and
generic elsewhere (`interface/wx/richmsgdlg.h:18-23`). On MSW every `TaskDialog`-based dialog
(`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`) ignores dark mode
(`interface/wx/app.h:1436-1440`).
(`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`) has followed dark mode
since 3.3.3 (`interface/wx/app.h:1442-1448`).
**OrcaSlicer.** UI code uses the MsgDialog family (`MessageDialog`, `RichMessageDialog`,
`WarningDialog`, `ErrorDialog`, `InfoDialog`; `references/windows-dialogs.md` §MsgDialog family) for
@@ -848,7 +852,7 @@ Orca does not use `wxConfig`/`wxFileConfig` (so the 3.3 change of the Unix defau
**Contract** (`interface/wx/secretstore.h`): on Unix it needs libsecret and a running secret service, so
always check `IsOk(&errmsg)` (`:193-210, 257`); libsecret is no longer required at run time
(`docs/changes.txt:533`), so a missing library or service shows up only as `IsOk()` false.
(`docs/changes.txt:726`), so a missing library or service shows up only as `IsOk()` false.
`GetDefault()` "may show a dialog to the user under some platforms, so it can take an arbitrarily long
time to return" (`:243-246`). One username per service (`:260-268`).
@@ -7,7 +7,7 @@ yields and nested event loops, progress dialogs, startup, shutdown and exception
screen, and the access rules for `wxGetApp()` and `app_config`. Read it whenever code runs off the
main thread, defers work, starts a timer, yields, shows progress, or runs during startup or shutdown.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth).
wx cites are relative to the pinned wx 3.3.4 tree (located as in `SKILL.md` §Ground truth).
Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`, so `wxASSERT`
is compiled out and `wxCHECK*` returns silently: a timer started off the main thread never fires on macOS,
`Exit()` on a loop that is not the active one is ignored, `Start(0)` on macOS fails — all without
@@ -454,12 +454,12 @@ and aborts the whole app. Wrap the entire body; log and convert to a status the
`wxMessageQueue::Post(T&&)` accepts move-only types (`interface/wx/msgqueue.h:90-97`).
- `wxTHREAD_WAIT_DEFAULT` is now `wxTHREAD_WAIT_BLOCK`; `wxTHREAD_WAIT_YIELD` "is dangerous" and "is
only implemented in wxMSW and simply ignored under the other platforms" (`interface/wx/thread.h:648-675`),
so the 3.3 default change (`docs/changes.txt:39-41`) only affects MSW code calling
so the 3.3 default change (`docs/changes.txt:45-47`) only affects MSW code calling
`Wait()`/`Delete()` from the GUI thread.
- `wxCRITICAL_SECTION(name)` declares a function-static critical section, which is initialised
safely only if the function is first called from the main thread before other threads call it;
otherwise make it a global (`interface/wx/thread.h:1762-1779`). Prefer `std::mutex`.
- `wxSecretStore` documents no thread-affinity notes in 3.3.2 (`interface/wx/secretstore.h`).
- `wxSecretStore` documents no thread-affinity notes in 3.3.4 (`interface/wx/secretstore.h`).
## The Jobs framework
@@ -829,8 +829,8 @@ nothing) (`interface/wx/utils.h:1203-1212, 1243-1248`; see `references/strings-i
**Platforms.** On MSW `wxProgressDialog` is native: a TaskDialog run by a
`wxProgressDialogTaskRunner` **wxThread**, yielding `ALL & ~USER_INPUT` (TIMER and THREAD included)
(`src/msw/progdlg.cpp` `DispatchEvents`) **[source]**; anything based on `TaskDialog()` has no dark
mode (`interface/wx/app.h:1436-1440`). On macOS and GTK it is `wxGenericProgressDialog`, which creates
(`src/msw/progdlg.cpp` `DispatchEvents`) **[source]**; the native `TaskDialog` follows wx dark mode
since 3.3.3 (`interface/wx/app.h:1442-1448`). On macOS and GTK it is `wxGenericProgressDialog`, which creates
a temporary event loop if none is active (`src/generic/progdlgg.cpp:360-366`). On MSW (native
dialog or Orca's generic one) and macOS, `CallAfter` bodies run inside `Update()`/`Pulse()`; on GTK
the masked yield runs no queued event, so they wait until control returns to an event loop
@@ -839,7 +839,7 @@ the masked yield runs no queued event, so they wait until control returns to an
**OrcaSlicer.**
- `Slic3r::GUI::ProgressDialog` (`Widgets/ProgressDialog.hpp`) is a themed fork of
`wxGenericProgressDialog`: same `YieldFor(UI | USER_INPUT)` semantics, temporary event loop when
none is active, generic on every platform (so no TaskDialog dark-mode gap).
none is active, generic on every platform (so it is themed by Orca, not the native TaskDialog).
- `detail::run_off_thread_with_progress` (`PluginsDialog.hpp`, wrapped by
`PluginsDialog::run_with_dialog`/`run_with_dialog_wait`): a heap wx `wxProgressDialog` (the native
TaskDialog on MSW, the generic dialog on macOS and GTK) pulsed by a
@@ -1,7 +1,7 @@
# WebView, OpenGL canvas, AUI docking and media
How wxWebView, wxGLCanvas/wxGLContext, wxAuiManager/wxAuiToolBar and the camera view behave in the
wx 3.3.2 tree Orca builds, and the Orca wrappers built on them: `WebView`, `WebViewHostDialog` and
wx 3.3.4 tree Orca builds, and the Orca wrappers built on them: `WebView`, `WebViewHostDialog` and
the web dialogs/panels, `OpenGLManager`, `GLCanvas3D` and its ImGui layer, the Plater docking
manager, `BBLTopbar`, `wxMediaCtrl3`. Read it before touching any browser view, GL canvas, docked
pane, the top bar or the camera view, or when debugging a blank web page, a black or garbled 3D
@@ -242,7 +242,7 @@ a single message handler and the Chromium and IE backends do not support script
| `Add…` return | `false` if *any* handler already exists, whatever its name (`src/msw/webview_edge.cpp:1635-1645`) | always `true` (`src/osx/webview_webkit.mm:620-629`); a duplicate name makes WebKit raise an NSException that cannot be caught and kills the app ([external]: Apple behaviour, recorded in the `WebView::CreateWebView` comment) | `false` on a duplicate or with no view, **but** `g_signal_connect` runs before registration, so a duplicate add leaves a second connection (`src/gtk/webview_webkit2.cpp:1638-1657`) |
| `Remove…` | clears the single name (`1647-1652`) | removes the native handler (`632-636`) | unregisters but **does not disconnect** the signal (`1659-1664`): Remove + Add delivers every later message **twice** |
| `window.<name>` alias | user script `window.<name> = window.chrome.webview` + `ExecuteScript` (`1030-1046`) | `AddUserScript` + **synchronous** `RunScript` (`626-628`) | `AddUserScript` + **synchronous** `RunScript` (`1650-1654`) |
| Delivery | queued with `AddPendingEvent` (`785-813`); [documented] "Queue Edge wxWebView events" (`docs/changes.txt:525`) | `ProcessWindowEvent` **inside** the `WKScriptMessageHandler` delegate (`1324-1361`) | `HandleWindowEvent` **inside** the GTK signal (`397-409`) |
| Delivery | queued with `AddPendingEvent` (`785-813`); [documented] "Queue Edge wxWebView events" (`docs/changes.txt:718`) | `ProcessWindowEvent` **inside** the `WKScriptMessageHandler` delegate (`1324-1361`) | `HandleWindowEvent` **inside** the GTK signal (`397-409`) |
| Event fields | handler name and event object set | name set, **no event object** | **empty `GetMessageHandler()`, no event object** |
| Body | `get_WebMessageAsJson`, JSON-decoded when it is a string (`798-809`) | NSString as is; dict/array → JSON; NSNumber → `stringValue` (booleans arrive as `1`/`0`); NSNull → `"null"` | object → JSON string, otherwise `ToString` (`64-94`) |
@@ -710,8 +710,8 @@ the environment variable `wx_opengl_egl=0` does the same; it exists only when `w
returns true only when EGL is in use (false when `wxHAS_EGL` is undefined); `GetGLXVersion()` returns 0
when GLX is not in use; both exist only when `wxHAS_GLX` is defined (`interface/wx/glcanvas.h:999-1030`).
`wxGLCanvas::CreateSurface()` was removed "as exposing it doesn't make sense when wxGLCanvas may use
either EGL or GLX" (`docs/changes.txt:250-252`); "Allow using GLX and EGL in same program (#26023)" is
an upstream 3.3.2 wxGTK change (`docs/changes.txt:290-292`).
either EGL or GLX" (`docs/changes.txt:282-284`); "Allow using GLX and EGL in same program (#26023)" is
an upstream 3.3.2 wxGTK change (`docs/changes.txt:485`).
**Source** [source]:
- When both backends are built, **EGL is the default even on X11** unless `PreferGLX()` or the
@@ -727,7 +727,7 @@ an upstream 3.3.2 wxGTK change (`docs/changes.txt:290-292`).
(`src/unix/glegl.cpp:801-860`).
- The EGL-based `wxGLCanvas` for Wayland dates from 3.1.5 (`docs/changes_32.txt:497`), so it predates
Orca's upgrade; its
high-DPI scale under EGL/Wayland was fixed in 3.3.0 (`docs/changes.txt:538`).
high-DPI scale under EGL/Wayland was fixed in 3.3.0 (`docs/changes.txt:731`).
**OrcaSlicer.**
- `deps/wxWidgets/wxWidgets.cmake` passes `-DwxUSE_GLCANVAS_EGL=ON` (also in the Flatpak manifest).
@@ -956,13 +956,13 @@ widgets beside the canvas (a sibling, not a child over it), or in a popup.
`src/aui/framemanager.cpp:910-920`; `OnPaneButton`); `DestroyOnClose` panes are detached and `Destroy()`ed
(`ClosePane`, `1431-1467`). With several handlers bound, each must `Skip()`.
- **3.3 changes.** `wxAUI_MGR_HINT_FADE` left the default flags (`interface/wx/aui/framemanager.h:43-50`;
`docs/changes.txt:132`); `wxAUI_MGR_LIVE_RESIZE` joined them (`interface/wx/aui/framemanager.h:59-62`); `AlwaysUsesLiveResize()` "always
`docs/changes.txt:138`); `wxAUI_MGR_LIVE_RESIZE` joined them (`interface/wx/aui/framemanager.h:59-62`); `AlwaysUsesLiveResize()` "always
returns false" as of 3.3.0 (`interface/wx/aui/framemanager.h:345`; `src/aui/framemanager.cpp:711-714`), non-live resize being
restored (`docs/changes.txt:490`) — the older "live resize is always used on wxOSX and GTK3/Wayland"
restored (`docs/changes.txt:683`) — the older "live resize is always used on wxOSX and GTK3/Wayland"
statement (`interface/wx/aui/framemanager.h:336-340`) is obsolete. Docking hints for floating frames use
`::wxGetMousePosition()` (`src/aui/framemanager.cpp:4139-4148`, `4242-4251`), which Wayland cannot supply.
- `wxAuiNotebook` (not used by Orca): page indices are logical; use `GetPagePosition()` for on-screen
order (`docs/changes.txt:121-126`); the default tab art changed (`35-37`).
order (`docs/changes.txt:128-132`); the default tab art changed (`41-43`).
**OrcaSlicer.** Orca's docks use `AuiMgr` (`AuiMgr.hpp`), a `wxAuiManager` whose `CreateFloatingFrame`
returns a `wxAuiFloatingFrame` that applies `UpdateFrameDarkUI`. `init(window)` calls
@@ -1034,8 +1034,10 @@ Plater (Prepare and Preview); `DesignPanel::m_aui` manages the Design tab's body
```
- **Rule:** Disable floating on Wayland (clear `wxAUI_MGR_ALLOW_FLOATING`, `Floatable(false)`, strip
floating state from saved layouts).
**Why:** AUI's floating and docking-hint logic needs global pointer positions and top-level window
moves (`wxGetMousePosition`, `wxEVT_MOVE`), which Wayland does not provide.
**Why:** AUI's docking-hint logic needs global pointer positions and top-level window moves
(`wxGetMousePosition`, `wxEVT_MOVE`), which Wayland does not provide. 3.3.4 added system drag sessions
so wx can move a pane's floating frame there anyway (`src/aui/framemanager.cpp` `StartDragSession`),
but the drop/docking hints still cannot work, so Orca keeps floating disabled.
## wxAuiToolBar and BBLTopbar
@@ -6,7 +6,7 @@ port; what Orca's `DPIDialog`/`DPIFrame` add; the Orca dialog recipe; and the `M
Read it before writing or reviewing any dialog, frame, close handler, `Destroy()`/`delete`, or code
that keeps a pointer to a window across an event, a `CallAfter` or a modal loop.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth). wx is
wx cites are relative to the pinned wx 3.3.4 tree (located as in `SKILL.md` §Ground truth). wx is
built with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every wx assert
quoted below is compiled out, so misuse fails silently (dropped call, stuck loop, freed memory), never with an assert dialog. "GTK" below
means wxGTK as Orca builds it on Linux: GTK3 by default (X11 or Wayland); GTK2 is only an opt-out.
@@ -424,7 +424,7 @@ mapped (`src/gtk/toplevel.cpp:1141-1243`).
**Raise.** "only requests the window manager to raise this window… If the window is currently hidden,
this function does *not* show it", top-level windows only (`interface/wx/window.h:3013-3033`); true on all ports
since 3.3 (`docs/changes.txt:144-146`). **[source]** MSW = `::SetForegroundWindow`, subject to the
since 3.3 (`docs/changes.txt:145-147`). **[source]** MSW = `::SetForegroundWindow`, subject to the
foreground lock — Windows may only flash the taskbar button (`src/msw/toplevel.cpp:650-655`); GTK =
`gtk_window_present` only if shown (`src/gtk/toplevel.cpp:1301-1310`; during a deferred X11 first show it
already counts as shown); macOS = `makeKeyAndOrderFront` only if shown (`src/osx/nonownedwnd_osx.cpp:289-295`, `src/osx/cocoa/nonownedwnd.mm:897-899`),
@@ -910,8 +910,9 @@ buttons are chosen — in numeric id order, so `{"Save", "OK"}` makes Save prima
**Rule.** Never `wxMessageBox`/`wxMessageDialog`/`wxRichMessageDialog` once the GUI exists; use the themed
replacements in `src/slic3r/GUI/MsgDialog.hpp` (`Slic3r::GUI`), rooted in `MsgDialog : DPIDialog` (logo on the
left, content on the right, `Button` row underneath, dark-mode and DPI aware). Why: on MSW the TaskDialog-based
native boxes (`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`) ignore dark mode
(`interface/wx/app.h:1436-1446`), and native boxes cannot match Orca's look. `wxMessageBox` remains only for
native boxes (`wxMessageBox`, `wxMessageDialog`, `wxRichMessageDialog`, `wxProgressDialog`) now follow dark mode
since 3.3.3, so only the date/time/calendar controls, `wxPrintDialog` on Windows 10 and MDI applications stay
light (`interface/wx/app.h:1442-1448`); native boxes cannot match Orca's look. `wxMessageBox` remains only for
failures before the GUI exists (e.g. `GUI_App::load_language`). Native message-box style limits:
`references/strings-i18n-files.md`. Return-value trap when reading old code: `wxMessageBox()` returns
`wxYES/wxNO/wxCANCEL/wxOK/wxHELP`, while `ShowModal()` returns `wxID_YES/…` (`interface/wx/msgdlg.h:269-276` vs `309-311`).
@@ -1,17 +1,19 @@
# wx 3.1.5 → 3.3.2: changes Orca code can trip on
# wx 3.1.5 → 3.3.4: changes Orca code can trip on
OrcaSlicer moved from wxWidgets **3.1.5** to **3.3.2** (8248b06337, #12941). This file digests the
OrcaSlicer moved from wxWidgets **3.1.5** to **3.3.2** (8248b06337, #12941) and the pin is now upstream
**3.3.4** (`deps/wxWidgets/wxWidgets.cmake`, tag `v3.3.4`). This file digests the
changes between those versions that matter to GUI code — the 3.1.6–3.2.0 incompatible changes, the
3.3 incompatible changes, and the notable 3.3.0/3.3.1/3.3.2 changes — each with what it means for
Orca, plus the migration already done in Orca as rules for new code. Read it when code written
from older wx knowledge behaves oddly, when a wx doc says "since 3.3", or when reviewing code near
the workarounds in §8. Where this file and prior wx knowledge disagree, this file wins.
3.3 incompatible changes, and the notable 3.3.0/3.3.1/3.3.2/3.3.3/3.3.4 changes — each with what it
means for Orca, plus the migration already done in Orca as rules for new code. Read it when code
written from older wx knowledge behaves oddly, when a wx doc says "since 3.3", or when reviewing code
near the workarounds in §10. Where this file and prior wx knowledge disagree, this file wins.
Contents: [Rules](#rules) · [1 Reading the change logs](#1-reading-the-change-logs) ·
[2 3.1.6 → 3.2.0](#2-changes-from-316-to-320) · [3 3.3 behaviour changes](#3-33-behaviour-changes-that-compile) ·
[4 3.3 build changes](#4-33-changes-that-break-the-build) · [5 3.3.0](#5-330-notable-changes) ·
[6 3.3.1](#6-331-notable-fixes) · [7 3.3.2](#7-332-notable-changes) ·
[8 Migration done in Orca](#8-migration-already-done-in-orca)
[8 3.3.3](#8-333-notable-changes) · [9 3.3.4](#9-334-notable-changes) ·
[10 Migration done in Orca](#10-migration-already-done-in-orca)
All `docs/`, `interface/`, `include/`, `src/`, `build/` cites are relative to the pinned wx tree
(located as in `SKILL.md` §Ground truth), except paths
@@ -21,8 +23,8 @@ explicitly called Orca's (`deps/…`, Orca's `src/CMakeLists.txt`) and bare Orca
1. Read every "now asserts" in the change logs as "now silently returns or does nothing" in Orca:
wx asserts are compiled out. Validate arguments yourself. → [§1](#1-reading-the-change-logs)
2. Target 3.3.2 only. Do not add `wxCHECK_VERSION` / `wxVERSION_NUMBER` /
`wxVERSION_EQUAL_OR_GREATER_THAN` branches for older wx. → [§8](#8-migration-already-done-in-orca)
2. Target 3.3.4 only. Do not add `wxCHECK_VERSION` / `wxVERSION_NUMBER` /
`wxVERSION_EQUAL_OR_GREATER_THAN` branches for older wx. → [§10](#10-migration-already-done-in-orca)
3. Mark every override of a wx virtual `override`. 3.2/3.3 changed parameter types (`wxBitmapBundle`,
`wxReadOnlyDC`, `wxWindowBase*`, `wxVersionContext`); a stale signature must fail to compile, not
become a silent overload. → [§4](#4-33-changes-that-break-the-build)
@@ -39,33 +41,36 @@ explicitly called Orca's (`deps/…`, Orca's `src/CMakeLists.txt`) and bare Orca
`WX_GL_SAMPLES`); `wxGLAttributes::Defaults()` no longer includes it. → [§3](#3-33-behaviour-changes-that-compile)
9. Size a `wxImageList` in physical pixels (the bitmaps' `GetSize()`), or use the `wxBitmapBundle`
image APIs. → [§3](#3-33-behaviour-changes-that-compile)
10. Return translated strings as `wxString` by value. → [§3](#3-33-behaviour-changes-that-compile)
10. `wxGetTranslation()` returns `const wxString&` (3.3.2 made it by-value; 3.3.3 reverted that); a
wrapper that converts a translation returns by value. → [§3](#3-33-behaviour-changes-that-compile)
11. Escape `&` in user text used as a control label or book page title. → [§3](#3-33-behaviour-changes-that-compile)
12. `wxDynamicCast` only on a pointer whose static type derives from `wxObject`; use `dynamic_cast`
for mixin interfaces (`wxComboPopup`, `wxItemContainer`, `wxTextEntry`). → [§4](#4-33-changes-that-break-the-build)
13. Build a `wxArrayString` with `Add()` or an initializer list, walk wx lists with
`compatibility_iterator` or range-for, and make a `wxString` the first operand of a mixed
`+` chain. → [§4](#4-33-changes-that-break-the-build)
14. MSW windows are not double-buffered by default in 3.3.2; a custom-painted control buffers itself.
14. MSW windows are not double-buffered by default in 3.3.4; a custom-painted control buffers itself.
→ [§5](#5-330-notable-changes)
15. `wxEVT_DPI_CHANGED` now fires on GTK3 too; such handlers must be correct on GTK, and every handler
you bind calls `Skip()` (`DPIAware`'s own is the one exception). → [§5](#5-330-notable-changes)
16. wx MSW dark mode cannot be switched at runtime; do not replace Orca's NppDarkMode with it without
keeping live theme switching. → [§5](#5-330-notable-changes)
16. wx MSW dark mode became runtime-switchable in 3.3.3/3.3.4 (`SetAppearance`/`MSWEnableDarkMode`
restyle existing windows); Orca still keeps NppDarkMode for its own chrome.
→ [§5](#5-330-notable-changes), [§9](#9-334-notable-changes)
17. After `wxStaticText::SetLabel`, re-wrap with `Wrap(-1); Wrap(w);` (or use `Label`); `wxST_WRAP`
is opt-in and works only inside a sizer. → [§7](#7-332-notable-changes)
18. On Linux/X11 call `wxGLCanvas::PreferGLX()` before any GL use; on Unix set a swap interval explicitly
if frame pacing matters (wx forces 0). → [§7](#7-332-notable-changes)
19. Compare `wxGrid::GetSelectedBlocks()` `begin()` with `end()` before dereferencing.
→ [§8](#8-migration-already-done-in-orca)
→ [§10](#10-migration-already-done-in-orca)
20. Keep the post-upgrade workarounds (MainFrame `WS_CAPTION` masking, macOS deep-link handler,
`MSWEnableDarkMode` ordering, `SidePopup` anchoring, MSW `GLCanvas3D::on_paint` render) when
touching that code. → [§8](#8-migration-already-done-in-orca)
touching that code. → [§10](#10-migration-already-done-in-orca)
## 1 Reading the change logs
**Sources.** `docs/changes.txt` lists changes since 3.2: incompatible behaviour changes :11-146,
build-breaking changes :149-252, 3.3.2 :255-330, 3.3.1 :333-377, 3.3.0 :380-622.
**Sources.** `docs/changes.txt` lists changes since 3.2: incompatible behaviour changes :11-178,
build-breaking changes :181-292, 3.3.4 :295-385, 3.3.3 :388-445, 3.3.2 :448-523, 3.3.1 :526-570,
3.3.0 :573-815.
`docs/changes_32.txt` covers 3.x → 3.2.0: the cumulative "INCOMPATIBLE CHANGES SINCE 3.0.x" list
:9-233, 3.2.0 :235-276, 3.1.7 :279-340, 3.1.6 :343-444. wxQt, wxiOS, wxUniv, wxMotif and wxGTK1
items are left out here.
@@ -73,11 +78,14 @@ items are left out here.
**Coverage gaps.**
- The "since 3.0.x" list in `changes_32.txt` is cumulative over all of 3.1.x. Most of it was already
in force in 3.1.5; §2.1 lists only what was added after 3.1.5, §2.3 the older items worth knowing.
- `changes_32.txt` in this tree stops at 3.2.0. The 3.2.1–3.2.10 maintenance fixes are not listed
- `changes_32.txt` in this tree stops at 3.2.0. The 3.2.1–3.2.12 maintenance fixes are not listed
anywhere in the tree.
- 3.3.0's list is relative to 3.2.8 (`changes.txt:383`). 3.3.2's list is relative to 3.2.10, and the
- 3.3.0's list is relative to 3.2.8 (`changes.txt:576`). 3.3.2's list is relative to 3.2.10, and the
full set of changes since 3.3.1 *also includes* the 3.2.9 and 3.2.10 fixes, which are **not**
listed in this file (`changes.txt:258-259`).
listed in this file (`changes.txt:451-452`). 3.3.3's list is relative to 3.2.11, and its changes
since 3.3.2 *also include* the 3.2.10–3.2.11 fixes (`changes.txt:391-392`); 3.3.4's list is
relative to 3.2.11 too, and its changes since 3.3.3 *also include* the 3.2.11–3.2.12 fixes
(`changes.txt:298-299`).
**"Asserts" mean silence in Orca.** wx is built with `-DwxBUILD_DEBUG_LEVEL=0`
(`deps/wxWidgets/wxWidgets.cmake` → `build/cmake/init.cmake:245-246`), and `libslic3r_gui` gets
@@ -117,7 +125,7 @@ the 3.1.7-specific section.
| `wxSpinCtrl::SetValue(wxString)` sends no events on MSW (as documented) | :129-130 | do not rely on setter events (`controls-dataview.md`) |
| `wxButton::GetBitmap{Current,Disabled,Focus,Pressed}()` return a valid bitmap on MSW only if set | :132-134 | check `IsOk()` |
| `wxFileName::GetVolume()` returns `\\share` for UNC paths and `\\?\Volume{GUID}` for GUID paths | :136-140 | path code comparing volumes |
| `wxBitmapComboBoxBase::SetItemBitmap()` takes `wxBitmapBundle` | :152-153 | the bundle change also retyped `OnAddBitmap` (5f365b5c6b, §8) |
| `wxBitmapComboBoxBase::SetItemBitmap()` takes `wxBitmapBundle` | :152-153 | the bundle change also retyped `OnAddBitmap` (5f365b5c6b, §10) |
| MSW also links `oleacc` (3.1.5 already needed `shlwapi`, `uxtheme`, `version`) | :158-163 | automatic with MSVC |
| Xcode projects drop i386, add arm64 | :205-207 | none (CMake build) |
| `wxImage` ctor from XPM data is `explicit` | :225-226 | write `wxImage(xpm)` |
@@ -136,7 +144,7 @@ the 3.1.7-specific section.
| MSW: TLW resizing on DPI change improved and overridable — a handler that sizes the TLW itself does not `Skip()` (3.2.0) | `changes_32.txt:269`; `interface/wx/event.h` (`wxDPIChangedEvent`) | Orca's `DPIAware` handler does not `Skip()` (`dpi-bitmaps-fonts.md`) |
| `wxUILocale` (3.1.6); `wxLocale::IsAvailable` is now implemented through it | `changes_32.txt:348`; `src/common/intl.cpp:740-781` | see `wxUILocale::IsSupported` in §3 |
| `wxWebView::RunScriptAsync()` (3.1.6) | `changes_32.txt:382` | `webview-gl-aui-media.md` |
| wxOSX: "Allow user input in `wxPopupTransientWindow`" (3.1.7) — the popup's child holds mouse capture while the cursor is outside it and releases it inside, toggled from `OnIdle` [source] | `changes_32.txt:334`; `src/common/popupcmn.cpp:438-471` | how wxOSX transient popups track the mouse; the macOS gap-dismissal worked around in `SidePopup::Popup` is not traced to a specific wx change (§8; `popups-menus.md`) |
| wxOSX: "Allow user input in `wxPopupTransientWindow`" (3.1.7) — the popup's child holds mouse capture while the cursor is outside it and releases it inside, toggled from `OnIdle` [source] | `changes_32.txt:334`; `src/common/popupcmn.cpp:438-471` | how wxOSX transient popups track the mouse; the macOS gap-dismissal worked around in `SidePopup::Popup` is not traced to a specific wx change (§10; `popups-menus.md`) |
| wxGTK: Wayland fixes (3.1.6), no GDK errors from `PopupMenu()` on Wayland (3.1.7), `wxCURSOR_SIZING` on Wayland (3.2.0) | `changes_32.txt:397, 317, 261` | `platforms.md` |
| MSW: all native modal dialogs are app-modal (3.1.6) | `changes_32.txt:409` | `windows-dialogs.md` |
| Also new: `wxKeyEvent::IsAutoRepeat()`, `wxSpinCtrl::GetTextValue()/SetIncrement()`, `wxTopLevelWindow::SetContentProtection()`, `wxEVT_SPLITTER_SASH_POS_RESIZE`, OSX full-screen view options, OSX `wxEVT_CHAR` from `wxDataViewCtrl` | `changes_32.txt:364-436` | available API |
@@ -167,7 +175,7 @@ These were already in force in 3.1.5, so the upgrade did not change them, but co
## 3 3.3 behaviour changes that compile
`changes.txt` "Changes in behaviour not resulting in compilation errors" (:11-146). Each row: the
`changes.txt` "Changes in behaviour not resulting in compilation errors" (:11-178). Each row: the
change, its line, and what it means for Orca.
| Change | Line | Orca relevance |
@@ -177,31 +185,30 @@ change, its line, and what it means for Orca.
| `wxGLCanvas` no longer multisamples by default | :21-23 | pitfall below |
| `wxFileConfig` on Unix defaults to XDG `~/.config/appname.conf` (old file still used; `wxCONFIG_USE_XDG`/`wxCONFIG_USE_HOME`, `MigrateLocalFile()`) | :25-29 | none: Orca uses `AppConfig`, no `wxConfig` (`strings-i18n-files.md`) |
| `wxColourDatabase` uses CSS values; `UseScheme()` restores the old ones (`interface/wx/gdicmn.h:845, 973-996`) | :31-33 | none: stock colours (`*wxGREEN`, …) are fixed RGB (`src/common/gdicmn.cpp:789-830`) and Orca builds colours from hex/RGB. Do not build colours from names |
| `wxAuiNotebook` default art is the new flat art; `wxAuiNativeTabArt` (or `"native"` in XRC) keeps the old look | :35-37 | none: no `wxAuiNotebook` in Orca |
| `wxTHREAD_WAIT_DEFAULT` is `wxTHREAD_WAIT_BLOCK`: `wxThread::Delete()/Wait()` no longer pump events | :39-41 | Orca uses std/boost threads (`wxThread` only for `IsMain()`); a new `wxThread` whose exit needs the main loop would deadlock |
| `wxDocument::OnCloseDocument()` runs once, after the views are destroyed (it used to run twice when the document was closed from the menu); an override must not rely on any view existing | :43-46 | none: no doc/view |
| `wxGrid::FreezeTo()` asserts on out-of-range counts, and freezes even when the grid is too small | :48-51 | silent `false` in Orca; it also refuses reordered or drag-movable rows/columns [source] (`src/generic/grid.cpp:5742-5750`). Clamp arguments (`controls-dataview.md`) |
| Invalid `wxImageList` calls assert | :53-56 | silent in Orca; `wxCHECK` returns (generic `Add` → -1). Create the list with a valid size before use |
| `wxTRANSPARENT_WINDOW` does nothing (`#define wxTRANSPARENT_WINDOW 0`, `include/wx/defs.h:1449`); MSW code that needs it can set `WS_EX_TRANSPARENT` | :58-60 | pitfall below; 026b105dcb |
| MSW `wxTextDataObject::SetData()` size includes the 2-byte NUL (consistent with `GetDataSize()`); an old-style size chops the last character | :62-66 | none: Orca uses the `wxTextDataObject(text)` ctor; use `SetText()`, never `SetData()` |
| `wxListCtrl::EditLabel()` asserts without `wxLC_EDIT_LABELS` | :68-69 | silent no-op in Orca; add the style where editing is intended |
| MSW `wxSystemAppearance::IsDark()` reports the app's own mode; `AreAppsDark()`/`IsSystemDark()` report the OS | :71-73 | pitfall below |
| Unix `wxUILocale::IsSupported()` no longer falls back to another region of the same language; pass just `"fr"` to accept any `fr_XX` | :75-79 | `wxLocale::IsAvailable` builds the region-qualified tag (`GetCanonicalWithRegion()`) and calls `IsSupported()` [source] (`src/common/intl.cpp:740-781`); `GUI_App::load_language` relies on it, so on Linux a language whose canonical locale (e.g. `fr_FR`) is not installed reports unavailable. Keep the fallbacks in `load_language` |
| Deprecated `wxPGCellRenderer::DrawCaptionSelectionRect()` overload not called; override the overload taking `wxWindow*`, or enable 3.0 compatibility | :81-83 | none: no propgrid in Orca's own code |
| `wxImageList` size is in physical pixels | :85-88 | pitfall below |
| Mac `wxWebRequest` no longer uses persistent storage. The entry names `wxWebRequest::EnablePersistentStorage()`; the real API is `wxWebSession::EnablePersistentStorage(bool)` (`interface/wx/webrequest.h:1612-1629`; also `wxWebSessionSync`, :1835), macOS-only, before the first request | :90-92 | none: Orca uses `wxWebRequest` only for image downloads (`wxWebSession::GetDefault().CreateRequest` in `StatusPanel`, `SliceInfoPanel`, `ReleaseNote`, `DeviceErrorDialog`); network and login agents use libcurl (`Slic3r::Http`) |
| MSW `wxBitmap::Create(size, dc)` no longer multiplies by the DC's content scale; the size is physical | :94-96 | remove compensating scaling; use `CreateWithLogicalSize` for logical sizes |
| `wxIMAGE_QUALITY_NEAREST` has a new value and is no longer `wxIMAGE_QUALITY_NORMAL`; `NORMAL` (the `Scale`/`Rescale` default) is bilinear + box average (`interface/wx/image.h:31-67`) | :98-99 | never store or compare the enum numerically; default-quality thumbnails and icons look smoother than under 3.1.5; pass `wxIMAGE_QUALITY_NEAREST` for pixel-exact scaling |
| `wxTextCtrl::{Save,Load}File()` treat `.rtf` as RTF | :101-104 | pass `wxTEXT_TYPE_PLAIN` to keep plain text |
| `wxClientDC`/`wxPaintDC` offset their origin by a `wxFrame` toolbar on every port | :106-109 | none: Orca frames have no native toolbar (`BBLTopbar` is a child `wxAuiToolBar`) |
| `wxTextCtrl::SetLabel()` does nothing and asserts on every port (MSW used to act as `SetValue`) | :111-113 | pitfall below |
| `wxAuiGenericTabArt` subclasses (also via `wxAuiMSWTabArt`) override `DrawPageTab()`/`GetPageTabSize()` instead of `DrawTab()`/`GetTabSize()`; direct `wxAuiTabArt` subclasses still work | :115-120 | none: no Orca tab art |
| `wxAuiNotebook` page index is logical (reorder-independent); `GetPagePosition()` gives the screen position | :122-126 | none; index math under `wxAUI_NB_TAB_MOVE` is what breaks |
| `wxListbook`/`wxChoicebook` interpret mnemonics in page titles "just as the other wx*book classes already did" | :128-130 | Orca uses neither (`BedShapeDialog` uses `wxSimplebook` + a combo); every book interprets `&` (`interface/wx/bookctrl.h:141-147`) → rule 11 |
| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :132-133 | Orca's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `AuiMgr::init`), so the docking hint no longer fades; add the flag if wanted |
| `wxPrintDialogData::SetAllPages(false)`/`SetSelection(false)` changed meaning | :135-137 | none |
| `wxGetTranslation()` returns `wxString` by value | :139-142 | pitfall below |
| `wxWindow::Raise()` no longer shows a hidden window on any port | :144-146 | pitfall below; ba867cc534 |
| `wxAuiNotebook` default art is the new flat art; `wxAuiNativeTabArt` (or `"native"` in XRC) keeps the old look | :41-43 | none: no `wxAuiNotebook` in Orca |
| `wxTHREAD_WAIT_DEFAULT` is `wxTHREAD_WAIT_BLOCK`: `wxThread::Delete()/Wait()` no longer pump events | :45-47 | Orca uses std/boost threads (`wxThread` only for `IsMain()`); a new `wxThread` whose exit needs the main loop would deadlock |
| `wxDocument::OnCloseDocument()` runs once, after the views are destroyed (it used to run twice when the document was closed from the menu); an override must not rely on any view existing | :49-52 | none: no doc/view |
| `wxGrid::FreezeTo()` asserts on out-of-range counts, and freezes even when the grid is too small | :54-57 | silent `false` in Orca; it also refuses reordered or drag-movable rows/columns [source] (`src/generic/grid.cpp:5742-5750`). Clamp arguments (`controls-dataview.md`) |
| Invalid `wxImageList` calls assert | :59-62 | silent in Orca; `wxCHECK` returns (generic `Add` → -1). Create the list with a valid size before use |
| `wxTRANSPARENT_WINDOW` does nothing (`#define wxTRANSPARENT_WINDOW 0`, `include/wx/defs.h:1449`); MSW code that needs it can set `WS_EX_TRANSPARENT` | :64-66 | pitfall below; 026b105dcb |
| MSW `wxTextDataObject::SetData()` size includes the 2-byte NUL (consistent with `GetDataSize()`); an old-style size chops the last character | :68-72 | none: Orca uses the `wxTextDataObject(text)` ctor; use `SetText()`, never `SetData()` |
| `wxListCtrl::EditLabel()` asserts without `wxLC_EDIT_LABELS` | :74-75 | silent no-op in Orca; add the style where editing is intended |
| MSW `wxSystemAppearance::IsDark()` reports the app's own mode; `AreAppsDark()`/`IsSystemDark()` report the OS | :77-79 | pitfall below |
| Unix `wxUILocale::IsSupported()` no longer falls back to another region of the same language; pass just `"fr"` to accept any `fr_XX` | :81-85 | `wxLocale::IsAvailable` builds the region-qualified tag (`GetCanonicalWithRegion()`) and calls `IsSupported()` [source] (`src/common/intl.cpp:740-781`); `GUI_App::load_language` relies on it, so on Linux a language whose canonical locale (e.g. `fr_FR`) is not installed reports unavailable. Keep the fallbacks in `load_language` |
| Deprecated `wxPGCellRenderer::DrawCaptionSelectionRect()` overload not called; override the overload taking `wxWindow*`, or enable 3.0 compatibility | :87-89 | none: no propgrid in Orca's own code |
| `wxImageList` size is in physical pixels | :91-94 | pitfall below |
| Mac `wxWebRequest` no longer uses persistent storage. The entry names `wxWebRequest::EnablePersistentStorage()`; the real API is `wxWebSession::EnablePersistentStorage(bool)` (`interface/wx/webrequest.h:1612-1629`; also `wxWebSessionSync`, :1835), macOS-only, before the first request | :96-98 | none: Orca uses `wxWebRequest` only for image downloads (`wxWebSession::GetDefault().CreateRequest` in `StatusPanel`, `SliceInfoPanel`, `ReleaseNote`, `DeviceErrorDialog`); network and login agents use libcurl (`Slic3r::Http`) |
| MSW `wxBitmap::Create(size, dc)` no longer multiplies by the DC's content scale; the size is physical | :100-102 | remove compensating scaling; use `CreateWithLogicalSize` for logical sizes |
| `wxIMAGE_QUALITY_NEAREST` has a new value and is no longer `wxIMAGE_QUALITY_NORMAL`; `NORMAL` (the `Scale`/`Rescale` default) is bilinear + box average (`interface/wx/image.h:31-67`) | :104-105 | never store or compare the enum numerically; default-quality thumbnails and icons look smoother than under 3.1.5; pass `wxIMAGE_QUALITY_NEAREST` for pixel-exact scaling |
| `wxTextCtrl::{Save,Load}File()` treat `.rtf` as RTF | :107-110 | pass `wxTEXT_TYPE_PLAIN` to keep plain text |
| `wxClientDC`/`wxPaintDC` offset their origin by a `wxFrame` toolbar on every port | :112-115 | none: Orca frames have no native toolbar (`BBLTopbar` is a child `wxAuiToolBar`) |
| `wxTextCtrl::SetLabel()` does nothing and asserts on every port (MSW used to act as `SetValue`) | :117-119 | pitfall below |
| `wxAuiGenericTabArt` subclasses (also via `wxAuiMSWTabArt`) override `DrawPageTab()`/`GetPageTabSize()` instead of `DrawTab()`/`GetTabSize()`; direct `wxAuiTabArt` subclasses still work | :121-126 | none: no Orca tab art |
| `wxAuiNotebook` page index is logical (reorder-independent); `GetPagePosition()` gives the screen position | :128-132 | none; index math under `wxAUI_NB_TAB_MOVE` is what breaks |
| `wxListbook`/`wxChoicebook` interpret mnemonics in page titles "just as the other wx*book classes already did" | :134-136 | Orca uses neither (`BedShapeDialog` uses `wxSimplebook` + a combo); every book interprets `&` (`interface/wx/bookctrl.h:141-147`) → rule 11 |
| `wxAUI_MGR_HINT_FADE` is not in the default `wxAuiManager` style | :138-139 | Orca's `AuiMgr` keeps the default flags (minus `wxAUI_MGR_ALLOW_FLOATING` on Wayland, `AuiMgr::init`), so the docking hint no longer fades; add the flag if wanted |
| `wxPrintDialogData::SetAllPages(false)`/`SetSelection(false)` changed meaning | :141-143 | none |
| `wxWindow::Raise()` no longer shows a hidden window on any port | :145-147 | pitfall below; ba867cc534 |
**Pitfalls**
@@ -277,7 +284,7 @@ change, its line, and what it means for Orca.
// Wrong: m_input_ip->GetTextCtrl()->SetLabelText(m_obj->get_dev_ip());
// Right: m_input_ip->GetTextCtrl()->ChangeValue(m_obj->get_dev_ip()); // no wxEVT_TEXT
```
Cite: `docs/changes.txt:111-113`; `controls-dataview.md`.
Cite: `docs/changes.txt:117-119`; `controls-dataview.md`.
- **Rule:** Size a `wxImageList` from the bitmaps' physical size, or use
`SetImages(std::vector<wxBitmapBundle>)`.
@@ -293,18 +300,23 @@ change, its line, and what it means for Orca.
// Right: wxBitmap bmp = create_scaled_bitmap("icon", this, 16);
// m_images = new wxImageList(bmp.GetWidth(), bmp.GetHeight(), false);
```
Cite: `docs/changes.txt:85-88`; `Tab` builds its list from `bmp().GetWidth()/GetHeight()`;
Cite: `docs/changes.txt:91-94`; `Tab` builds its list from `bmp().GetWidth()/GetHeight()`;
`dpi-bitmaps-fonts.md`.
- **Rule:** A function that returns a translation returns `wxString` by value.
**Why:** `wxGetTranslation()` returns by value now (`include/wx/translation.h:278-321`); returning
it as `const wxString&` dangles. Orca's `I18N::translate` overloads and `_L` already return by
value.
- **Rule:** A wrapper that converts a translation returns by value; `wxGetTranslation()` itself
returns `const wxString&`.
**Why:** 3.3.2 changed `wxGetTranslation()` to return `wxString` by value, and **3.3.3 reverted it**
(`interface/wx/translation.h:615`, `include/wx/translation.h:277-309`), so the return type is the
3.2.x `const wxString&` again. That reference points at the catalog's own string or at
`wxTranslations::GetUntranslatedString()`, both of which outlive the call, so forwarding it as a
reference is safe. What still dangles is a temporary you construct: `wxString::FromUTF8`,
`ToUTF8()`, `format_wxstr`. Orca's `I18N::translate` overloads return `wxString` by value and
`translate_utf8`/`_u8L` return `std::string`.
```cpp
// Wrong: const wxString& title() { return _L("Printer"); }
// Right: wxString title() { return _L("Printer"); }
// Wrong: const wxString& path() { return wxString::FromUTF8(raw); } // temporary
// Right: wxString path() { return wxString::FromUTF8(raw); }
```
Cite: `docs/changes.txt:139-142`; `I18N.hpp`.
Cite: §8 (the 3.3.3 revert); `I18N.hpp`.
- **Rule:** Escape `&` in user data (preset, filament, printer, file names) shown as a control label or
book page title.
@@ -314,37 +326,37 @@ change, its line, and what it means for Orca.
// Wrong: book->AddPage(page, preset_name);
// Right: book->AddPage(page, wxControl::EscapeMnemonics(preset_name)); // or label->SetLabelText(name)
```
Cite: `docs/changes.txt:128-130`; `interface/wx/bookctrl.h:141-147`; `controls-dataview.md`.
Cite: `docs/changes.txt:134-136`; `interface/wx/bookctrl.h:141-147`; `controls-dataview.md`.
## 4 3.3 changes that break the build
`changes.txt` "Changes in behaviour which may result in build errors" (:149-252).
`changes.txt` "Changes in behaviour which may result in build errors" (:181-292).
| Change | Line | Orca relevance |
|---|---|---|
| 3.0-deprecated symbols disabled by default (`WXWIN_COMPATIBILITY_3_0=1` at wx build time re-enables them), 2.8-deprecated removed | :152-154 | Orca builds `WXWIN_COMPATIBILITY_3_0 0`, `_3_2 1`: port off 3.0-deprecated API |
| `wxUSE_UNICODE=0` unsupported | :156 | none |
| `wxUSE_STD_CONTAINERS=1` by default ("Container Classes" overview); building wx with 0 keeps the old containers | :158-161 | Orca builds with 1 → pitfall below |
| `wxUSE_STL` gone; implicit `wxString` → `std::[w]string` only with `wxUSE_STD_STRING_CONV_IN_WXSTRING=1` at wx build time | :163-166 | Orca builds 0: convert explicitly (`into_u8`, `ToStdString`, `ToUTF8()`) |
| MSW links `gdiplus.lib`, `msimg32.lib` | :168-172 | automatic with MSVC/wx-config; only static non-MSVC builds add them |
| wxMotif, wxGTK1 removed | :174-175 | none |
| Private containers (e.g. `wxSimpleDataObjectList`) removed; object arrays (`wxImageArray`) compare values in `Index()` | :177-183 | use `std::vector`/`std::list` |
| Operators on wx types are hidden (not global) | :185-189 | pitfall below |
| `wxString` from `std::string_view` makes `wxstr = {"Hello", 2}` ambiguous | :191-194 | write `wxString{"Hello", 2}` |
| Generic `wxSearchCtrl` lost multi-line-only methods | :196-197 | none |
| Wide-filename `wxOnAssert()` overload removed | :199-200 | none |
| 64-bit DLLs carry an `x64` suffix in all build systems | :202-204 | packaging scripts matching DLL names; Orca links wx statically (Flatpak builds it shared in its own manifest) |
| CMake config installs to `lib/cmake/wxWidgets-3.3`; plain `find_package(wxWidgets)` is unaffected, hard-coded paths break | :206-211 | Orca: `find_package(wxWidgets 3.3 CONFIG …)` on Windows/macOS, `wx-config --toolkit=gtk${SLIC3R_GTK}` on Linux (Orca's `src/CMakeLists.txt`) |
| Memory-tracing options removed | :213-216 | use ASan |
| `wxTEST_DIALOG()` needs a trailing `;` | :218-219 | none |
| `wxWindow::GetDefaultBorderForControl()` not virtual | :221-223 | do not override; use `wxBORDER_THEME` |
| GTK `wxDirButton::Create()` lost `wildcard` | :225-226 | none |
| Several virtuals take `wxReadOnlyDC` | :228-231 | pitfall below |
| `wxSizer::Detach()` takes `wxWindowBase*` | :233-236 | only custom sizer subclasses |
| `wx/cursor.h` no longer includes `wx/utils.h` | :238-240 | include `<wx/utils.h>` explicitly (8248b06337 added it to `GLCanvas3D.cpp`) |
| `wxStyledTextCtrl::AddSelection()` returns void | :242-244 | none: `wxUSE_STC=OFF` |
| `wxColour` from `bool` no longer compiles | :246-248 | use the RGB or string ctor explicitly |
| `wxGLCanvas::CreateSurface()` removed from EGL builds | :250-252 | none: wx chooses EGL or GLX itself (§7) |
| 3.0-deprecated symbols disabled by default (`WXWIN_COMPATIBILITY_3_0=1` at wx build time re-enables them), 2.8-deprecated removed | :184-186 | Orca builds `WXWIN_COMPATIBILITY_3_0 0`, `_3_2 1`: port off 3.0-deprecated API |
| `wxUSE_UNICODE=0` unsupported | :188 | none |
| `wxUSE_STD_CONTAINERS=1` by default ("Container Classes" overview); building wx with 0 keeps the old containers | :190-193 | Orca builds with 1 → pitfall below |
| `wxUSE_STL` gone; implicit `wxString` → `std::[w]string` only with `wxUSE_STD_STRING_CONV_IN_WXSTRING=1` at wx build time | :195-198 | Orca builds 0: convert explicitly (`into_u8`, `ToStdString`, `ToUTF8()`) |
| MSW links `gdiplus.lib`, `msimg32.lib` | :200-204 | automatic with MSVC/wx-config; only static non-MSVC builds add them |
| wxMotif, wxGTK1 removed | :206-207 | none |
| Private containers (e.g. `wxSimpleDataObjectList`) removed; object arrays (`wxImageArray`) compare values in `Index()` | :209-215 | use `std::vector`/`std::list` |
| Operators on wx types are hidden (not global) | :217-221 | pitfall below |
| `wxString` from `std::string_view` makes `wxstr = {"Hello", 2}` ambiguous | :223-226 | write `wxString{"Hello", 2}` |
| Generic `wxSearchCtrl` lost multi-line-only methods | :228-229 | none |
| Wide-filename `wxOnAssert()` overload removed | :231-232 | none |
| 64-bit DLLs carry an `x64` suffix in all build systems | :234-236 | packaging scripts matching DLL names; Orca links wx statically (Flatpak builds it shared in its own manifest) |
| CMake config installs to `lib/cmake/wxWidgets-3.3`; plain `find_package(wxWidgets)` is unaffected, hard-coded paths break | :238-243 | Orca: `find_package(wxWidgets 3.3 CONFIG …)` on Windows/macOS, `wx-config --toolkit=gtk${SLIC3R_GTK}` on Linux (Orca's `src/CMakeLists.txt`) |
| Memory-tracing options removed | :245-248 | use ASan |
| `wxTEST_DIALOG()` needs a trailing `;` | :250-251 | none |
| `wxWindow::GetDefaultBorderForControl()` not virtual | :253-255 | do not override; use `wxBORDER_THEME` |
| GTK `wxDirButton::Create()` lost `wildcard` | :257-258 | none |
| Several virtuals take `wxReadOnlyDC` | :260-263 | pitfall below |
| `wxSizer::Detach()` takes `wxWindowBase*` | :265-268 | only custom sizer subclasses |
| `wx/cursor.h` no longer includes `wx/utils.h` | :270-272 | include `<wx/utils.h>` explicitly (8248b06337 added it to `GLCanvas3D.cpp`) |
| `wxStyledTextCtrl::AddSelection()` returns void | :274-276 | none: `wxUSE_STC=OFF` |
| `wxColour` from `bool` no longer compiles | :278-280 | use the RGB or string ctor explicitly |
| `wxGLCanvas::CreateSurface()` removed from EGL builds | :282-284 | none: wx chooses EGL or GLX itself (§7) |
**Pitfalls**
@@ -366,7 +378,7 @@ change, its line, and what it means for Orca.
// Wrong: wxSize GetToolSize(wxDC& dc, wxWindow* w, const wxAuiToolBarItem& it); // never called
// Right: wxSize GetToolSize(wxReadOnlyDC& dc, wxWindow* w, const wxAuiToolBarItem& it) override;
```
Cite: `docs/changes.txt:228-231`; `painting-custom-widgets.md`.
Cite: `docs/changes.txt:260-263`; `painting-custom-widgets.md`.
- **Rule:** With `wxUSE_STD_CONTAINERS=1`, build a `wxArrayString` with `Add()` or an initializer
list, and walk `WX_DECLARE_LIST` lists with `compatibility_iterator` or range-for.
@@ -386,7 +398,7 @@ change, its line, and what it means for Orca.
`wchar_t`, `std::string` or `std::wstring`.
**Why:** wx operators are hidden friends now, found only by argument-dependent lookup on a wx type;
"preventing them from implicitly being used with types convertible to wx types"
(`changes.txt:185-189`). `char + std::wstring + …` compiled under 3.1.5 through the global
(`changes.txt:217-221`). `char + std::wstring + …` compiled under 3.1.5 through the global
`operator+(char, const wxString&)`, converting the `std::wstring` implicitly; under 3.3 that
operator is a hidden friend (`include/wx/string.h:2150`) and no operand is a `wxString`, so it
does not.
@@ -413,9 +425,9 @@ change, its line, and what it means for Orca.
## 5 3.3.0 notable changes
`changes.txt:380-592` (relative to 3.2.8).
`changes.txt:573-785` (relative to 3.2.8).
**Major changes** (:385-394)
**Major changes** (:578-587)
| Change | Orca relevance |
|---|---|
@@ -429,7 +441,7 @@ change, its line, and what it means for Orca.
| Native GTK file dialogs when possible (#24486, #25104) | the portal dialog is used only with GTK ≥ 3.20 at runtime, without `wxFD_PREVIEW` and without an extra control [source] (`src/gtk/filedlg.cpp:265-273, 438-443`); Orca's `CheckboxFileDialog` (`SetExtraControlCreator`, `GUI_Utils.hpp`) therefore gets the non-native GTK dialog |
| Native `wxTextCtrl` contents / RTF (#24626, #24912) | available (`GetRTFValue`, `SearchText`) |
**All** (:398-442)
**All** (:591-635)
| Change | Orca relevance |
|---|---|
@@ -439,7 +451,7 @@ change, its line, and what it means for Orca.
| Environment variables use UTF-8 (#25101) | check round-tripping of non-ASCII paths through `wxGetEnv`/`wxSetEnv` |
| Improved locale matching (#24855); thread-safe `wxPlatformInfo::Get()` (#25459); `wxXmlParseError` from `wxXmlDocument::Load()` (#24215); customisable error exit code (#24770) | available |
**All (GUI)** (:444-500)
**All (GUI)** (:637-693)
| Change | Orca relevance |
|---|---|
@@ -452,17 +464,17 @@ change, its line, and what it means for Orca.
| Non-live resize restored in wxAUI and `wxSplitterWindow` (#24193) | `wxAUI_MGR_LIVE_RESIZE` is in `wxAUI_MGR_DEFAULT` since 3.3.0 (`interface/wx/aui/framemanager.h:59-66`). The style table's "always enabled in wxGTK3 and wxOSX ports as non-live resizing is not implemented in them" (:199-206) is as stale as the `AlwaysUsesLiveResize()` note: that function "always returns false" as of 3.3.0 (:345; `src/aui/framemanager.cpp:711-714`), and the flag decides on every port [source] (`HasLiveResize`, :716-719). See `webview-gl-aui-media.md` §AUI docking |
| `wxInfoBar::ShowCheckBox()` (#25394); printing multiple page ranges (#25030); `wxGrid::CopySelection()` (#24124); new default flat AUI tab art (#25316); `wxApp::SetAppearance()` (#24461) | available |
**All (WebView)** (:510-525) — `wxWebViewConfiguration` + `GetNativeConfiguration()`, `SetProxy()`,
**All (WebView)** (:703-718) — `wxWebViewConfiguration` + `GetNativeConfiguration()`, `SetProxy()`,
`ShowDevTools()`, `EnablePersistentStorage()`, `EnableBrowserAcceleratorKeys()`, clearing browsing
data, advanced requests, child-window handling, `IsTargetMainFrame()`, Edge user agent settable after
creation, and **Edge events queued** (#22744, #19075): Edge handlers run later than on the other
backends. Edge posts its events with `AddPendingEvent` (script messages: `src/msw/webview_edge.cpp:811`)
except the vetoable ones, `wxEVT_WEBVIEW_NAVIGATING` (:599) and `wxEVT_WEBVIEW_NEWWINDOW` with its
`NEWWINDOW_FEATURES` follow-up (:723, :737), which stay synchronous; WebKit and
backends. Edge posts its events with `AddPendingEvent` (script messages: `src/msw/webview_edge.cpp:872`)
except the vetoable ones, `wxEVT_WEBVIEW_NAVIGATING` (:655-662) and `wxEVT_WEBVIEW_NEWWINDOW` with its
`NEWWINDOW_FEATURES` follow-up (:783-785, :796-798), which stay synchronous; WebKit and
WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:1360`,
`src/gtk/webview_webkit2.cpp:408`) [source]. `webview-gl-aui-media.md` owns the details.
**wxGTK** (:527-547)
**wxGTK** (:720-740)
| Change | Orca relevance |
|---|---|
@@ -477,46 +489,53 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
| `wxDC::DrawRoundedRectangle()` radius limited to half the smaller side (#24327) | the clamp is in the GTK2 GDK DC (`src/gtk/dcclient.cpp:874-875`, built only in the GTK2 opt-out, `GTK2_LOWLEVEL_SRC` in `build/files`) and also in the common `wxGraphicsPathData::AddRoundedRectangle` (`src/common/graphcmn.cpp:439-440`, absent in 3.1.5) that `wxGraphicsContext::DrawRoundedRectangle` uses, so every `wxGCDC` clamps: GTK3 (Cairo) and macOS window DCs, and Orca's memory-DC + `wxGCDC` paint paths on all ports [source]. A radius larger than half the smaller side is now clamped instead of drawing overlapping arcs |
| Read-only `wxBitmapComboBox` height fixed (#25468) | `Slic3r::GUI::BitmapComboBox` on GTK |
**wxMSW** (:549-583)
**wxMSW** (:742-776)
| Change | Orca relevance |
|---|---|
| "Enable double buffering for all windows" (#22851) | **reverted in 3.3.2** (#25808) → pitfall below |
| `wxOverlay` reimplemented with layered windows (#23261) | none: no `wxOverlay` |
| `wxBG_STYLE_TRANSPARENT` implemented (#23412) | as `WS_EX_TRANSPARENT` on non-TLW children [source] (`src/msw/window.cpp:1581-1582`); set before `Create()` (`painting-custom-widgets.md`) |
| `wxCAPTION` turned on when min/max/close boxes are set (#23575) | `WS_CAPTION` is added to the style (`src/msw/toplevel.cpp:132-135`); origin of the MainFrame workaround (§8) |
| `wxCAPTION` turned on when min/max/close boxes are set (#23575) | `WS_CAPTION` is added to the style (`src/msw/toplevel.cpp:132-135`); origin of the MainFrame workaround (§10) |
| `wxButton` default size larger in high DPI (#25297) | raw `wxButton` layouts may grow; Orca dialogs use `Button`/`DialogButtons` |
| Markup in `wxStaticText` (#25000); `wxHyperlinkCtrl` colour changeable (#23549) | `SetLabelMarkup` now works on MSW (single line) |
| Extended-length paths (#25033); `wxFileDialog` no unwanted extension (#24949); UTF-8 build fixes (#23313); non-BMP strings (#25128) | available |
| `wxDisplay` invalidated on display change (#25396); TLW with one child resized on DPI change (#22983); Aero-snapped geometry saved | multi-monitor geometry |
| Modern default `wxTreeCtrl` look (#23844); RTL fixes for `wxOverlay`/`wxScrolled` (#25413) and GDI+ (#25431); `wxBitmap::UseAlpha()` returns `bool` (#23919) | available |
**wxOSX** (:585-592) — `wxEventLoop::OnExit()` is always called (#25409); TLW cursor setting fixed
**wxOSX** (:778-785) — `wxEventLoop::OnExit()` is always called (#25409); TLW cursor setting fixed
(#25131); Cmd-C no longer activates a "Close" button (#25346); `wxCursor` loadable from resources
(#24374); `wxUIActionSimulator` works (#23692), usable for GUI tests.
**Pitfalls**
- **Rule:** Do not replace Orca's MSW dark mode with wx's (`SetAppearance`/`MSWEnableDarkMode`)
unless live theme switching is kept.
**Why:** wx's MSW dark mode cannot change once any top-level window exists or a mode was chosen:
`SetAppearance` returns `CannotChange` (`src/msw/darkmode.cpp:263-270`;
`interface/wx/app.h:1166-1173`). TaskDialog-based dialogs (`wxMessageDialog`, `wxProgressDialog`,
simple `wxAboutBox`), the common dialogs (colour, find/replace, font, page setup, print) and the
date/time/calendar controls stay light (`interface/wx/app.h:1434-1448`). Orca switches themes at
runtime, which is why 8248b06337 kept NppDarkMode and only informs wx with
`MSWEnableDarkMode(DarkMode_Auto)` (§8).
- **Rule:** Orca keeps its own Windows theming (`NppDarkMode`) and only informs wx with
`MSWEnableDarkMode(DarkMode_Auto)`; do not replace it with wx's dark mode without accounting for the
chrome NppDarkMode themes itself.
**Why:** when 8248b06337 landed (3.3.2), wx's MSW dark mode could not be changed once a top-level
window existed — `SetAppearance` returned `CannotChange` — so a live Preferences toggle would have
been restart-only. That blocker is gone: 3.3.3 (#26516) and 3.3.4 (#26734) made the mode switchable
at runtime. `MSWEnableDarkMode()` re-applies the mode to an existing window (sends it
`WM_SYSCOLORCHANGE`, `src/msw/darkmode.cpp:345-349`), controls restyle through
`wxMSWDarkMode::HasChanged()`/`NotifySysColorChange()` (`src/msw/window.cpp:5337, 5528-5534`), and
`SetAppearance()` returns only `Ok`/`Failure`; `AppearanceResult::CannotChange` is no longer
returned (`src/msw/darkmode.cpp:289-375`; `interface/wx/app.h:1151, 1179-1183`). The remaining
documented limits are the date/time/calendar controls, `wxPrintDialog` on Windows 10 and MDI
applications (`interface/wx/app.h:1442-1448`); `wxMessageDialog` (3.3.3, #26570) and the common
dialogs (3.3.4, #26780) now do support dark mode. Orca still keeps NppDarkMode because it themes
Orca's own chrome (title bars, explorer theme, scrollbars, list headers) and drives the live
theme-switch path (§10).
Cite: 8248b06337 (PR text); `colours-dark-mode.md`.
- **Rule:** On MSW, a custom-painted control buffers its own drawing (`wxAutoBufferedPaintDC`/
`wxBufferedPaintDC` with `wxBG_STYLE_PAINT`, or the Orca memory-DC + `wxGCDC` path), or calls
`SetDoubleBuffered(true)` after creation.
**Why:** 3.3.0's global `WS_EX_COMPOSITED` was reverted in 3.3.2 (`changes.txt:308`). In 3.3.2
**Why:** 3.3.0's global `WS_EX_COMPOSITED` was reverted in 3.3.2 (`changes.txt:501`). In 3.3.4
nothing sets it except an explicit `SetDoubleBuffered(true)` [source] (`src/msw/window.cpp:4704-4721`;
the only other use is `MSWDisableComposited`, :1643-1652), so MSW windows are not double-buffered by
default, exactly as in 3.1.5. `wxAutoBufferedPaintDC` is a `wxBufferedPaintDC` on MSW at compile
time (`include/wx/dcbuffer.h:18-23, 215-221`). Nothing tuned against 3.3.0/3.3.1 applies.
Cite: `docs/changes.txt:308, 557`; `painting-custom-widgets.md`.
Cite: `docs/changes.txt:501, 750`; `painting-custom-widgets.md`.
- **Rule:** A `wxEVT_DPI_CHANGED` handler must be correct on GTK3, and every handler you bind — on a
child, a control or a `DPIDialog`/`DPIFrame` — calls `Skip()`.
@@ -529,11 +548,11 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
ctor starts at 1 because `get_dpi_for_window` returns 96 on Linux). Double scaling when a window moves between monitors of
different scale is a risk that has not been verified at runtime; test DPI changes on GTK when
touching `DPIAware::rescale` paths.
Cite: `docs/changes.txt:541`; `dpi-bitmaps-fonts.md` §wxEVT_DPI_CHANGED.
Cite: `docs/changes.txt:734`; `dpi-bitmaps-fonts.md` §wxEVT_DPI_CHANGED.
## 6 3.3.1 notable fixes
`changes.txt:333-377`.
`changes.txt:526-570`.
| Port | Change | Orca relevance |
|---|---|---|
@@ -547,7 +566,7 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
## 7 3.3.2 notable changes
`changes.txt:255-318`, relative to 3.2.10 (see §1 for the unlisted 3.2.9/3.2.10 fixes).
`changes.txt:448-511`, relative to 3.2.10 (see §1 for the unlisted 3.2.9/3.2.10 fixes).
| Port | Change | Orca relevance |
|---|---|---|
@@ -589,7 +608,7 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
// Wrong: m_static_valid->SetLabel(info_line); m_static_valid->Wrap(FromDIP(300));
// Right: m_static_valid->SetLabel(info_line); m_static_valid->Wrap(-1); m_static_valid->Wrap(FromDIP(300));
```
Cite: `docs/changes.txt:280`; `sizers-layout.md` §wxStaticText wrapping.
Cite: `docs/changes.txt:473`; `sizers-layout.md` §wxStaticText wrapping.
- **Rule:** On Linux/X11, call `wxGLCanvas::PreferGLX()` before any GL use, attribute objects
included; set a swap interval explicitly if the canvas needs VSync.
@@ -605,7 +624,7 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
```cpp
// Right (if frame pacing is wanted): canvas->SetSwapInterval(1); // before the first SwapBuffers
```
Cite: `docs/changes.txt:275-276, 292`; `webview-gl-aui-media.md` §EGL vs GLX.
Cite: `docs/changes.txt:468-469, 485`; `webview-gl-aui-media.md` §EGL vs GLX.
- **Rule:** Do not count clicks from `wxEVT_LEFT_DCLICK` alone on macOS; handle `DOWN` and `DCLICK`
as on MSW.
@@ -613,13 +632,68 @@ WebKit2GTK deliver script messages synchronously (`src/osx/webview_webkit.mm:136
(left and right buttons), so a triple click gives `DOWN, DCLICK, DOWN` as on MSW; previously every
click with `clickCount > 1` was a `DCLICK` [source] (`src/osx/cocoa/window.mm:4103-4150`). Per-platform click-count ifdefs
written for 3.1.5 need rechecking.
Cite: `docs/changes.txt:316`; `mouse-keyboard-focus.md`.
Cite: `docs/changes.txt:509`; `mouse-keyboard-focus.md`.
## 8 Migration already done in Orca
## 8 3.3.3 notable changes
The upgrade landed as 8248b06337 ("Updated wxWidgets to 3.3.2", #12941; build system 2d7e26292b).
Its PR kept Orca's own MSW dark mode to avoid broader changes and because wx's needs an app restart to
switch. Each row is a rule for new code; the owning file has the detail.
`changes.txt:388-445`, relative to 3.2.11 (see §1 for the unlisted 3.2.10/3.2.11 fixes).
| Port | Change | Orca relevance |
|---|---|---|
| All | `wxMoveToTrash()` (#26256); `wxSizer::DetachItem()` (#26512); truncated reads are errors in `wxDataInputStream` (#26600); `wxGetOsDescription()` (#26231); C++20 `<=>` for `wxString` (#26306) | available; Orca detaches windows and has its own file deletion |
| All | **`wxGetTranslation()` returns `const wxString&` again**, reverting 3.3.2's by-value change | the by-value rule is retired → §3 pitfall |
| GUI | `wxWebView::Print()` print settings (#26239) and `PrintToPDF()` (#26583); `wxWebViewConfiguration::DisableGPUAcceleration()` (#26626) | `webview-gl-aui-media.md` |
| GUI | `wxGrid::GetFrozen{Row,Col}LabelWindow()` (#26617); `wxSVGFileDC` accessibility (#26379) and `GetSVGDocument()` (#26344) | `controls-dataview.md`; `wxSVGFileDC` available |
| wxGTK | Fix regression when using `wxImageList` in high DPI (#26349) | the physical-pixel image-list rule is unchanged (§3) |
| wxGTK | Fix `wxStaticText` label wrapping | the base `Wrap()` cache is unchanged (§7); only the GTK label wrapping was fixed |
| wxGTK | Assert when the clipboard has non-ASCII targets fixed (#26263); GTK-without-Wayland build fixed (#26269); high-DPI bitmaps in `wxAboutBox` (#22192) | clipboard |
| wxMSW | **Light/dark mode switching implemented** (#26516) | see §5: this removes the old "cannot switch at runtime" blocker |
| wxMSW | `wxMessageDialog` dark mode (#26570); combo/listbox dark mode on Windows 11 (#26535); `wxCheckBox` label in screen readers (#26270); `wxPD_CAN_SKIP` regression (#26287); Direct2D text regression (#26259) | dark-mode and accessibility fixes |
| wxMSW | Stylus events (#26223); colour emoji via Direct2D (#26657) | available |
| wxOSX | Data-view icons under macOS 26 (#26374); `wxOverlay` phantoms in Mission Control (#26380); `wxEVT_DATAVIEW_ITEM_EDITING_DONE` value set (#26367); Xcode project for macOS 27 (#26578) | native look and data view |
## 9 3.3.4 notable changes
`changes.txt:295-385`, relative to 3.2.11 (see §1 for the unlisted 3.2.11/3.2.12 fixes).
| Port | Change | Orca relevance |
|---|---|---|
| All | `wxString` usable with `{fmt}`/`std::format()` (#27180); `wxPATH_RMDIR_PARENTS`; `fsync()` in `wxTempFile` (#27001); `wxDateTime::GetAsDOS()` after 2038 (#4393); TCP `wxIPC` fixes (#24858) | available |
| GUI | **Accelerators take priority over the focused window in all ports** (#22630), with the new `wxEVT_ACCELERATOR_KEY` event and the `wxWindow::ClaimsKeyBeforeAccelerator()` override that text controls use to reserve their editing keys (`interface/wx/event.h:1799`, `interface/wx/window.h:4580`) | pitfall below; `mouse-keyboard-focus.md` |
| GUI | wxGrid: `wxGridCellChoiceEditor` UI fixes (#12094), best size for auto-wrapped rows (#14072), `MakeCellVisible()` with variable row heights (#4371), editor-change crash (#21986) | `controls-dataview.md`; the choice-editor `Combo()` cast rule is unchanged |
| GUI | AUI: no spurious sash jump on click (#4547); an added pane's best size is respected (#4404) | Plater docking |
| GUI | `wxListCtrl` (#15411) and `wxStatusBar` (#24305) accessibility; `wxAuiNotebook` selection after a page removal (#9920); `wxPreviewCanvas` crash (#9822); generic `wxCalendarCtrl` background colour (#21885); LunaSVG with filters (#27176) | available; no `wxAuiNotebook` in Orca |
| wxMSW | **Dark mode can be enabled dynamically** (#26734); dark mode for `wxFontPickerCtrl` (#26667), `wxNotebook` (#26694, #26695), `wxSpinButton` (#26682, #26683) and the common dialogs (#26780) | §5 pitfall; `colours-dark-mode.md` |
| wxMSW | `wxRenameFile()` no longer copies file attributes (`changes.txt:345-347`, incompatible); Esc closes dialogs used by IME/auto-completion (#16059, #27145); IME handling in wxGrid improved (#24558); `wxPersistent` controls DPI-aware (#18752); non-BMP character input (#27016) | file dialogs, keyboard and IME |
| wxGTK | Wayland: crash on pointer/seat disconnect fixed (#27069); **AUI panes draggable via system drag sessions** (#26969); **window geometry restorable** via XDG session management (#26324); overlay-scrollbar client size (#26889) | the Wayland gaps are partly narrowed; `webview-gl-aui-media.md` §AUI |
| wxOSX | `wxEVT_CHAR` key codes fixed for events with Ctrl (#26810); context-menu accessibility (#13010); custom controls in sandboxed `wxFileDialog` (#26908) | `mouse-keyboard-focus.md`; macOS keys |
**Pitfalls**
- **Rule:** On every port an accelerator now runs before the focused window handles the key, unless
that window claims the key; a custom control that must keep a key over an accelerator overrides
`wxWindow::ClaimsKeyBeforeAccelerator()`, or handles `wxEVT_ACCELERATOR_KEY` and calls
`UseAccelerator()`.
**Why:** before 3.3.4 only wxGTK let the focused window handle the key first; the other ports
already gave accelerators priority. 3.3.4 made every port consistent and added the
`wxEVT_ACCELERATOR_KEY` event, sent after `wxEVT_CHAR_HOOK` (before it on macOS) to the focus
window and propagated to its parents (`interface/wx/event.h:1740-1801`;
`interface/wx/window.h:4554-4583`). Text controls, `wxComboBox` and `wxSpinCtrl` reserve their
editing keys by overriding `ClaimsKeyBeforeAccelerator()` and returning
`wxTextEntryBase::IsUsedForEditing()` (`src/common/textcmn.cpp:136-140`;
`src/common/textentrycmn.cpp:441-509`; `src/common/spinctrlcmn.cpp:160-167`;
`src/msw/combobox.cpp:389-395`, `src/gtk/combobox.cpp:205-211`), and the GTK accelerator walk now
consults it (`src/gtk/window.cpp:1524-1539`). Orca's topbar menus are popups whose accelerators are
display-only and Orca dispatches its own shortcuts, so few sites are affected; the macOS
`ObjectList` `wxAcceleratorTable` and the wxInspector table are the live ones.
Cite: `docs/changes.txt:321`; `mouse-keyboard-focus.md` §Accelerators.
## 10 Migration already done in Orca
The upgrade landed as 8248b06337 ("Updated wxWidgets to 3.3.2", #12941; build system 2d7e26292b) and
the pin later advanced to upstream 3.3.4. Its PR kept Orca's own MSW dark mode to avoid broader
changes; wx could then only switch at app start, so a live Orca toggle needed NppDarkMode. Each row is
a rule for new code; the owning file has the detail.
| Commit | Rule for new code | Where | Owner |
|---|---|---|---|
@@ -0,0 +1,38 @@
diff --git a/build/cmake/wxWidgetsConfig.cmake.in b/build/cmake/wxWidgetsConfig.cmake.in
index 2f3a735d920c..2593c045b89b 100644
--- a/build/cmake/wxWidgetsConfig.cmake.in
+++ b/build/cmake/wxWidgetsConfig.cmake.in
@@ -50,12 +50,28 @@ if(WIN32_MSVC_NAMING)
set(wxARCH_SUFFIX "_${wxPLATFORM_ARCH}")
endif()
- # use wxWidgets_USE_STATIC to force static libraries, otherwise shared is searched first
- if(NOT wxWidgets_USE_STATIC AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}_dll/@PROJECT_NAME@Targets.cmake")
- set(wxPLATFORM_LIB_DIR "/${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}_dll")
- elseif(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}_lib/@PROJECT_NAME@Targets.cmake")
- set(wxPLATFORM_LIB_DIR "/${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}_lib")
+ # wxCOMPILER_PREFIX names the consuming compiler; the installed layout is named
+ # after the compiler that built wxWidgets. cl and clang-cl share an ABI, so
+ # either can consume either build.
+ set(wxCOMPILER_PREFIXES "${wxCOMPILER_PREFIX}")
+ if(wxCOMPILER_PREFIX STREQUAL "clang")
+ list(APPEND wxCOMPILER_PREFIXES "vc")
+ elseif(wxCOMPILER_PREFIX STREQUAL "vc")
+ list(APPEND wxCOMPILER_PREFIXES "clang")
endif()
+
+ # use wxWidgets_USE_STATIC to force static libraries, otherwise shared is searched first
+ foreach(wxPREFIX IN LISTS wxCOMPILER_PREFIXES)
+ if(NOT wxWidgets_USE_STATIC AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/${wxPREFIX}${wxARCH_SUFFIX}_dll/@PROJECT_NAME@Targets.cmake")
+ set(wxPLATFORM_LIB_DIR "/${wxPREFIX}${wxARCH_SUFFIX}_dll")
+ break()
+ elseif(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${wxPREFIX}${wxARCH_SUFFIX}_lib/@PROJECT_NAME@Targets.cmake")
+ set(wxPLATFORM_LIB_DIR "/${wxPREFIX}${wxARCH_SUFFIX}_lib")
+ break()
+ endif()
+ endforeach()
+ unset(wxPREFIX)
+ unset(wxCOMPILER_PREFIXES)
endif()
include("${CMAKE_CURRENT_LIST_DIR}${wxPLATFORM_LIB_DIR}/@PROJECT_NAME@Targets.cmake")
+8 -2
View File
@@ -28,12 +28,18 @@ if (APPLE)
COMMAND ${GIT_EXECUTABLE} apply --verbose
${CMAKE_CURRENT_LIST_DIR}/0001-macos-use-srgb-colour-components.patch
)
elseif (WIN32)
set(_wx_patch_command
${GIT_EXECUTABLE} checkout -f -- build/cmake/wxWidgetsConfig.cmake.in
COMMAND ${GIT_EXECUTABLE} apply --verbose
${CMAKE_CURRENT_LIST_DIR}/0002-clang-cl-sibling-compiler-prefix.patch
)
endif ()
orcaslicer_add_cmake_project(
wxWidgets
GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets"
GIT_TAG v3.3.2
GIT_REPOSITORY "https://github.com/wxWidgets/wxWidgets"
GIT_TAG v3.3.4
GIT_SHALLOW ON
GIT_SUBMODULES 3rdparty/catch 3rdparty/pcre 3rdparty/libwebp
PATCH_COMMAND ${_wx_patch_command}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Anker",
"version": "02.04.00.05",
"version": "02.04.00.06",
"force_update": "0",
"description": "Anker configurations",
"machine_model_list": [
@@ -304,6 +304,26 @@
"name": "Generic PLA Silk @Anker 0.25 nozzle",
"sub_path": "filament/Generic PLA Silk @Anker 0.25 nozzle.json"
},
{
"name": "AnkerMake PLA+ Basic @Anker base",
"sub_path": "filament/AnkerMake PLA+ Basic @Anker base.json"
},
{
"name": "AnkerMake PLA+ Glitter @Anker base",
"sub_path": "filament/AnkerMake PLA+ Glitter @Anker base.json"
},
{
"name": "AnkerMake PLA+ Matte @Anker base",
"sub_path": "filament/AnkerMake PLA+ Matte @Anker base.json"
},
{
"name": "AnkerMake PLA+ Metallic @Anker base",
"sub_path": "filament/AnkerMake PLA+ Metallic @Anker base.json"
},
{
"name": "AnkerMake PLA+ Silk @Anker base",
"sub_path": "filament/AnkerMake PLA+ Silk @Anker base.json"
},
{
"name": "Generic PLA+ @Anker",
"sub_path": "filament/Generic PLA+ @Anker.json"
@@ -327,6 +347,26 @@
{
"name": "Generic TPU @Anker",
"sub_path": "filament/Generic TPU @Anker.json"
},
{
"name": "AnkerMake PLA+ Basic @Anker",
"sub_path": "filament/AnkerMake PLA+ Basic @Anker.json"
},
{
"name": "AnkerMake PLA+ Glitter @Anker",
"sub_path": "filament/AnkerMake PLA+ Glitter @Anker.json"
},
{
"name": "AnkerMake PLA+ Matte @Anker",
"sub_path": "filament/AnkerMake PLA+ Matte @Anker.json"
},
{
"name": "AnkerMake PLA+ Metallic @Anker",
"sub_path": "filament/AnkerMake PLA+ Metallic @Anker.json"
},
{
"name": "AnkerMake PLA+ Silk @Anker",
"sub_path": "filament/AnkerMake PLA+ Silk @Anker.json"
}
],
"machine_list": [
@@ -0,0 +1,50 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Basic @Anker base",
"inherits": "Generic PLA+ @Anker base",
"from": "system",
"filament_id": "OFUuZlQL",
"instantiation": "false",
"filament_vendor": [
"AnkerMake"
],
"nozzle_temperature": [
"200"
],
"nozzle_temperature_initial_layer": [
"230"
],
"hot_plate_temp": [
"65"
],
"textured_plate_temp": [
"65"
],
"hot_plate_temp_initial_layer": [
"65"
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_flow_ratio": [
"1"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_cooling_layer_time": [
"60"
],
"slow_down_layer_time": [
"8"
],
"slow_down_min_speed": [
"30"
],
"default_filament_colour": [
"#FFFFFF"
],
"filament_max_volumetric_speed": [
"22"
]
}
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Basic @Anker",
"inherits": "AnkerMake PLA+ Basic @Anker base",
"from": "system",
"setting_id": "EfM5rmn4w6pMBajC",
"instantiation": "true",
"compatible_printers": [
"Anker M5 0.4 nozzle",
"Anker M5 0.6 nozzle",
"Anker M5 All-Metal 0.4 nozzle",
"Anker M5 All-Metal 0.6 nozzle",
"Anker M5C 0.4 nozzle",
"Anker M5C 0.6 nozzle"
]
}
@@ -0,0 +1,50 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Glitter @Anker base",
"inherits": "Generic PLA+ @Anker base",
"from": "system",
"filament_id": "OFxCURdg",
"instantiation": "false",
"filament_vendor": [
"AnkerMake"
],
"nozzle_temperature": [
"200"
],
"nozzle_temperature_initial_layer": [
"230"
],
"hot_plate_temp": [
"65"
],
"textured_plate_temp": [
"65"
],
"hot_plate_temp_initial_layer": [
"65"
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_flow_ratio": [
"1"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_cooling_layer_time": [
"60"
],
"slow_down_layer_time": [
"6"
],
"slow_down_min_speed": [
"30"
],
"default_filament_colour": [
"#00AD66"
],
"filament_max_volumetric_speed": [
"22"
]
}
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Glitter @Anker",
"inherits": "AnkerMake PLA+ Glitter @Anker base",
"from": "system",
"setting_id": "blGHp0htgHEj7khE",
"instantiation": "true",
"compatible_printers": [
"Anker M5 0.4 nozzle",
"Anker M5 0.6 nozzle",
"Anker M5 All-Metal 0.4 nozzle",
"Anker M5 All-Metal 0.6 nozzle",
"Anker M5C 0.4 nozzle",
"Anker M5C 0.6 nozzle"
]
}
@@ -0,0 +1,50 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Matte @Anker base",
"inherits": "Generic PLA+ @Anker base",
"from": "system",
"filament_id": "OFKDOyNK",
"instantiation": "false",
"filament_vendor": [
"AnkerMake"
],
"nozzle_temperature": [
"210"
],
"nozzle_temperature_initial_layer": [
"230"
],
"hot_plate_temp": [
"65"
],
"textured_plate_temp": [
"65"
],
"hot_plate_temp_initial_layer": [
"65"
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_flow_ratio": [
"1"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_cooling_layer_time": [
"60"
],
"slow_down_layer_time": [
"6"
],
"slow_down_min_speed": [
"30"
],
"default_filament_colour": [
"#FFFFFF"
],
"filament_max_volumetric_speed": [
"22"
]
}
@@ -0,0 +1,13 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Matte @Anker",
"inherits": "AnkerMake PLA+ Matte @Anker base",
"from": "system",
"setting_id": "tPgwC1eEXyWPOn9S",
"instantiation": "true",
"compatible_printers": [
"Anker M5 0.4 nozzle",
"Anker M5 All-Metal 0.4 nozzle",
"Anker M5C 0.4 nozzle"
]
}
@@ -0,0 +1,56 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Metallic @Anker base",
"inherits": "Generic PLA+ @Anker base",
"from": "system",
"filament_id": "OFBfbmtb",
"instantiation": "false",
"filament_vendor": [
"AnkerMake"
],
"nozzle_temperature": [
"200"
],
"textured_plate_temp": [
"60"
],
"textured_plate_temp_initial_layer": [
"60"
],
"filament_flow_ratio": [
"1"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_cooling_layer_time": [
"60"
],
"slow_down_layer_time": [
"6"
],
"slow_down_min_speed": [
"30"
],
"overhang_fan_speed": [
"50"
],
"reduce_fan_stop_start_freq": [
"0"
],
"filament_retraction_length": [
"1"
],
"filament_retraction_speed": [
"50"
],
"filament_deretraction_speed": [
"50"
],
"default_filament_colour": [
"#ACAC8A"
],
"filament_max_volumetric_speed": [
"20"
]
}
@@ -0,0 +1,13 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Metallic @Anker",
"inherits": "AnkerMake PLA+ Metallic @Anker base",
"from": "system",
"setting_id": "jlbAMNNaEacdUASZ",
"instantiation": "true",
"compatible_printers": [
"Anker M5 0.4 nozzle",
"Anker M5 All-Metal 0.4 nozzle",
"Anker M5C 0.4 nozzle"
]
}
@@ -0,0 +1,62 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Silk @Anker base",
"inherits": "Generic PLA+ @Anker base",
"from": "system",
"filament_id": "OFkxTGQc",
"instantiation": "false",
"filament_vendor": [
"AnkerMake"
],
"nozzle_temperature": [
"210"
],
"nozzle_temperature_initial_layer": [
"230"
],
"hot_plate_temp": [
"65"
],
"textured_plate_temp": [
"65"
],
"hot_plate_temp_initial_layer": [
"65"
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_flow_ratio": [
"1"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_cooling_layer_time": [
"60"
],
"slow_down_layer_time": [
"2"
],
"slow_down_min_speed": [
"20"
],
"filament_retraction_length": [
"0.5"
],
"filament_retraction_speed": [
"20"
],
"filament_wipe": [
"0"
],
"default_filament_colour": [
"#99ADA3"
],
"filament_deretraction_speed": [
"20"
],
"filament_max_volumetric_speed": [
"13"
]
}
@@ -0,0 +1,15 @@
{
"type": "filament",
"name": "AnkerMake PLA+ Silk @Anker",
"inherits": "AnkerMake PLA+ Silk @Anker base",
"from": "system",
"setting_id": "eVM9Ckwjl5vEvEKN",
"instantiation": "true",
"compatible_printers": [
"Anker M5 0.4 nozzle",
"Anker M5 All-Metal 0.4 nozzle",
"Anker M5 All-Metal 0.6 nozzle",
"Anker M5C 0.4 nozzle",
"Anker M5C 0.6 nozzle"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Anycubic",
"version": "02.04.00.08",
"version": "02.04.00.09",
"force_update": "0",
"description": "Anycubic configurations",
"machine_model_list": [
@@ -110,7 +110,7 @@
"default_filament_profile": [
"Generic PLA @Anycubic"
],
"machine_start_gcode": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG0 Z0.2 F1800 ; move nozzle to print position\nG92 E0 ; specify current extruder position as zero\nG1 Y10 X180 E50 F1200 ; extrude a line in front of the printer\nG92 E0 ; specify current extruder position as zero\nG0 Z20 F6000 ; move head up\nG1 E-7 F2400 ; retract\nG04 S2 ; wait 2s\nG0 X0 F6000 ; wipe from oozed filament\nG1 E-1 F2400 ; undo some of the retraction to avoid oozing\nG1 F6000 ; set travel speed to move to start printing point\nM117",
"machine_start_gcode": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG0 Z0.2 F1800 ; move nozzle to print position\nG92 E0 ; specify current extruder position as zero\nM109 S[nozzle_temperature_initial_layer] ; wait for nozzle temp to stabilize\nG1 Y10 X180 E50 F1200 ; extrude a line in front of the printer\nG92 E0 ; specify current extruder position as zero\nG0 Z20 F6000 ; move head up\nG1 E-7 F2400 ; retract\nG04 S2 ; wait 2s\nG0 X0 F6000 ; wipe from oozed filament\nG1 E-1 F2400 ; undo some of the retraction to avoid oozing\nG1 F6000 ; set travel speed to move to start printing point\nM117",
"machine_end_gcode": "G4 ; wait\nG92 E0\nG1{if max_layer_z < printable_height} Z{z_offset+min(max_layer_z+30, printable_height)}{endif} ; move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200 F3000 ; home X axis\nM84 ; disable motors",
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]",
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Bambulab",
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
"version": "02.08.00.13",
"version": "02.08.00.14",
"force_update": "0",
"description": "the initial version of BBL configurations",
"machine_model_list": [
@@ -8,6 +8,7 @@
"0.4"
],
"printer_variant": "0.4",
"use_3mf": "1",
"bed_exclude_area": [
"0x0",
"28x0",
@@ -9,6 +9,7 @@
"0.4"
],
"printer_variant": "0.4",
"use_3mf": "1",
"best_object_pos": "0.3x0.5",
"bed_exclude_area": [],
"bed_temperature_formula": "by_highest_temp",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Prusa",
"version": "02.04.00.16",
"version": "02.04.00.17",
"force_update": "0",
"description": "Prusa configurations",
"machine_model_list": [
@@ -45,7 +45,7 @@
"35"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
],
"hot_plate_temp": [
"110"
@@ -78,7 +78,7 @@
"35"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
],
"hot_plate_temp": [
"110"
@@ -50,7 +50,7 @@
"0.8"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_wipe": [
"1"
@@ -38,7 +38,7 @@
"15"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One|CORE One L).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_z_hop": [
"0.6"
@@ -58,7 +58,7 @@
"7"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[0]==0.4}0.03{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[0]==0.4}0.03{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_z_hop": [
"0.6",
@@ -63,7 +63,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.04{elsif nozzle_diameter[filament_extruder_id]==0.25}0.1{elsif nozzle_diameter[filament_extruder_id]==0.3}0.06{elsif nozzle_diameter[filament_extruder_id]==0.35}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.03{elsif nozzle_diameter[filament_extruder_id]==0.6}0.02{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.02{elsif nozzle_diameter[filament_extruder_id]==0.5}0.018{elsif nozzle_diameter[filament_extruder_id]==0.6}0.012{elsif nozzle_diameter[filament_extruder_id]==0.8}0.01{elsif nozzle_diameter[filament_extruder_id]==0.25}0.09{elsif nozzle_diameter[filament_extruder_id]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp"
],
"filament_type": [
"ASA"
@@ -65,7 +65,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
],
"filament_type": [
"PA11-CF"
@@ -63,7 +63,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
],
"filament_type": [
"PC"
@@ -65,7 +65,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.6}0.025{elsif nozzle_diameter[filament_extruder_id]==0.8}0.016{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp"
],
"filament_type": [
"PC-CF"
@@ -69,7 +69,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.07{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.09{elsif nozzle_diameter[filament_extruder_id]==0.35}0.08{elsif nozzle_diameter[filament_extruder_id]==0.6}0.04{elsif nozzle_diameter[filament_extruder_id]==0.5}0.05{elsif nozzle_diameter[filament_extruder_id]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.053{elsif nozzle_diameter[filament_extruder_id]==0.5}0.042{elsif nozzle_diameter[filament_extruder_id]==0.6}0.032{elsif nozzle_diameter[filament_extruder_id]==0.8}0.018{elsif nozzle_diameter[filament_extruder_id]==0.25}0.18{elsif nozzle_diameter[filament_extruder_id]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_type": [
"PETG"
@@ -64,7 +64,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One|CORE One L).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_type": [
"PLA"
@@ -65,7 +65,7 @@
"1"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_type": [
"PVB"
@@ -64,7 +64,7 @@
"0"
],
"filament_start_gcode": [
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|CORE One).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
"M900 K{if nozzle_diameter[filament_extruder_id]==0.4}0.05{elsif nozzle_diameter[filament_extruder_id]==0.25}0.14{elsif nozzle_diameter[filament_extruder_id]==0.3}0.07{elsif nozzle_diameter[filament_extruder_id]==0.35}0.06{elsif nozzle_diameter[filament_extruder_id]==0.6}0.03{elsif nozzle_diameter[filament_extruder_id]==0.5}0.035{elsif nozzle_diameter[filament_extruder_id]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*(MK4IS|XLIS|MK4S|MK3.9S|COREONE).*/}\nM572 S{if nozzle_diameter[filament_extruder_id]==0.4}0.036{elsif nozzle_diameter[filament_extruder_id]==0.5}0.025{elsif nozzle_diameter[filament_extruder_id]==0.6}0.022{elsif nozzle_diameter[filament_extruder_id]==0.8}0.014{elsif nozzle_diameter[filament_extruder_id]==0.25}0.12{elsif nozzle_diameter[filament_extruder_id]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp"
],
"filament_type": [
"PLA"
@@ -118,9 +118,9 @@ modules:
# Use git instead of archive: wxWidgets 3.3 relies on multiple git
# submodules (PCRE2, etc.) that are not included in GitHub tarballs.
- type: git
url: https://github.com/SoftFever/Orca-deps-wxWidgets.git
tag: orca-3.3.2
commit: db1005db3dea2c37a46fb455a9a02e37aa360751
url: https://github.com/wxWidgets/wxWidgets.git
tag: v3.3.4
commit: 6a42a4b999b127cf28a8b5fe686da6b8ca93a9b8
# OrcaSlicer C++ dependencies (built offline with pre-downloaded archives)
- name: orca_deps
+1
View File
@@ -405,6 +405,7 @@ set(lisbslic3r_sources
Polyline.hpp
PresetBundle.cpp
PresetBundle.hpp
PrinterBedAssets.hpp
PresetCacheFormat.cpp
PresetCacheFormat.hpp
Preset.cpp
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#include <string>
namespace Slic3r {
+308 -2
View File
@@ -18,6 +18,7 @@
#include <cstdint>
#include <cmath>
#include <ctime>
#include <ios>
#include <optional>
#include <exception>
#include <memory>
@@ -29,6 +30,7 @@
#include <sstream>
#include "PresetBundle.hpp"
#include "PrinterBedAssets.hpp"
#include "Config.hpp"
#include "Exception.hpp"
@@ -64,6 +66,7 @@
#include <boost/algorithm/clamp.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
@@ -1678,6 +1681,258 @@ bool PresetBundle::apply_vendor_config(
return true;
}
namespace {
// The two bed files a printer bundle can carry. Zip names are bed/<n>/<stem><ext>, never the source basename.
struct BedAssetSlot {
const char *option;
const char *stem;
};
constexpr BedAssetSlot k_bed_asset_slots[] = {
{"bed_custom_texture", "texture"},
{"bed_custom_model", "model"},
};
std::string zip_slashes(std::string name)
{
for (char &c : name)
if (c == '\\')
c = '/';
return name;
}
// The bed dialog loads only png/svg textures and stl models. Anything else is not a bed asset.
bool bed_extension_allowed(const char *option, const std::string &ext)
{
if (std::string(option) == "bed_custom_texture")
return boost::iequals(ext, ".png") || boost::iequals(ext, ".svg");
if (std::string(option) == "bed_custom_model")
return boost::iequals(ext, ".stl");
return false;
}
// Extension including the dot, or empty when this option may not use it.
std::string bed_asset_extension(const std::string &path, const char *option)
{
const auto slash = path.find_last_of("/\\");
const auto base = slash == std::string::npos ? 0 : slash + 1;
const auto dot = path.find_last_of('.');
if (dot == std::string::npos || dot < base || dot + 1 >= path.size())
return {};
const std::string ext = path.substr(dot);
if (!bed_extension_allowed(option, ext))
return {};
return ext;
}
bool utf8_is_file(const std::string &path)
{
if (path.empty())
return false;
boost::system::error_code ec;
#ifdef _WIN32
// A narrow path is the ANSI code page unless nowide_filesystem() has run.
const std::wstring wide = boost::nowide::widen(path);
if (wide.empty())
return false;
return boost::filesystem::is_regular_file(boost::filesystem::path(wide), ec) && !ec;
#else
return boost::filesystem::is_regular_file(path, ec) && !ec;
#endif
}
std::string bed_asset_dest_utf8(const fs::path &dest)
{
#ifdef _WIN32
return boost::nowide::narrow(dest.wstring());
#else
return dest.string();
#endif
}
// True only for bed/<digits>/<stem><ext> with an extension the bed dialog accepts for this field.
bool accepted_bed_zip_name(const std::string &raw, const BedAssetSlot &slot, const fs::path &bundle_dir,
std::string &zip_name, std::string &folder)
{
const std::string name = zip_slashes(raw);
if (name.find("..") != std::string::npos)
return false;
if (name.size() < 8 || name.compare(0, 4, "bed/") != 0)
return false;
const auto slash = name.find('/', 4);
if (slash == std::string::npos || slash == 4 || name.find('/', slash + 1) != std::string::npos)
return false;
const std::string digits = name.substr(4, slash - 4);
for (unsigned char c : digits) {
if (!std::isdigit(c))
return false;
}
const std::string filename = name.substr(slash + 1);
const std::string ext = bed_asset_extension(filename, slot.option);
if (ext.empty() || filename != std::string(slot.stem) + ext)
return false;
if (!is_path_within_root(name, bundle_dir))
return false;
zip_name = name;
folder = digits;
return true;
}
struct PendingBedAsset {
std::string option;
std::string zip_name;
std::string dest;
std::string printer_key;
fs::path final_path;
fs::path staged;
};
// Validated asset names only. A raw manifest value that fails the check is not skipped later,
// so a value like printer/Name.json cannot hide that preset.
// printer_asset is keyed by printer_config zip entry. A flat (unkeyed) object is not read.
std::vector<PendingBedAsset> pending_bed_assets(const json &bundle_json, const fs::path &bundle_dir,
std::unordered_set<std::string> &skip_names,
const std::unordered_set<std::string> &zip_entries)
{
std::vector<PendingBedAsset> pending;
if (!bundle_json.contains("printer_asset") || !bundle_json["printer_asset"].is_object())
return pending;
const json &assets = bundle_json["printer_asset"];
for (auto it = assets.begin(); it != assets.end(); ++it) {
const std::string printer_key = it.key();
if (zip_entries.count(printer_key) == 0) {
BOOST_LOG_TRIVIAL(warning) << "Printer bundle asset entry is ignored, no matching printer in the zip: " << printer_key;
continue;
}
if (!it.value().is_object())
continue;
const json &per_printer = it.value();
for (const BedAssetSlot &slot : k_bed_asset_slots) {
if (!per_printer.contains(slot.option) || !per_printer[slot.option].is_string())
continue;
const std::string raw = per_printer[slot.option].get<std::string>();
std::string zip_name;
std::string folder;
if (!accepted_bed_zip_name(raw, slot, bundle_dir, zip_name, folder)) {
BOOST_LOG_TRIVIAL(warning) << "Printer bundle asset path is rejected: " << raw;
continue;
}
const std::string filename = zip_name.substr(zip_name.rfind('/') + 1);
const fs::path final_path = fs::absolute(bundle_dir / "bed" / folder / filename);
pending.push_back({slot.option, zip_name, bed_asset_dest_utf8(final_path), printer_key, final_path, {}});
skip_names.insert(zip_name);
}
}
return pending;
}
// Stage under temp_folder. The bundle bed/ directory is not touched until a printer preset keeps the path.
bool stage_bed_asset(mz_zip_archive &zip, PendingBedAsset &asset, const fs::path &temp_folder)
{
size_t size = 0;
void *buf = mz_zip_reader_extract_file_to_heap(&zip, asset.zip_name.c_str(), &size, MZ_ZIP_FLAG_CASE_SENSITIVE);
if (buf == nullptr) {
BOOST_LOG_TRIVIAL(warning) << "Printer bundle asset is missing, skipping " << asset.option << ": " << asset.zip_name;
return false;
}
const std::string filename = asset.zip_name.substr(asset.zip_name.rfind('/') + 1);
asset.staged = temp_folder / "bed" / asset.final_path.parent_path().filename() / filename;
boost::system::error_code ec;
fs::create_directories(asset.staged.parent_path(), ec);
const std::string staged_utf8 = bed_asset_dest_utf8(asset.staged);
bool wrote = false;
if (!ec) {
boost::nowide::ofstream out(staged_utf8, std::ios::binary | std::ios::trunc);
wrote = static_cast<bool>(out);
if (wrote && size > 0)
wrote = static_cast<bool>(out.write(static_cast<const char *>(buf), static_cast<std::streamsize>(size)));
}
mz_free(buf);
if (!wrote) {
BOOST_LOG_TRIVIAL(warning) << "Printer bundle asset could not be written, skipping " << asset.option << ": " << staged_utf8;
fs::remove(asset.staged, ec);
return false;
}
return true;
}
bool install_bed_asset(const PendingBedAsset &asset)
{
boost::system::error_code ec;
fs::create_directories(asset.final_path.parent_path(), ec);
if (!ec)
fs::copy_file(asset.staged, asset.final_path, fs::copy_options::overwrite_existing, ec);
if (ec) {
BOOST_LOG_TRIVIAL(warning) << "Printer bundle asset could not be installed: " << asset.dest;
return false;
}
return true;
}
bool is_pending_bed_asset_entry(const std::string &entry, const std::unordered_set<std::string> &names)
{
return names.count(entry) != 0 || names.count(zip_slashes(entry)) != 0;
}
bool printer_kept_bed_paths(const PresetCollection &printers, const std::map<std::string, std::string> &paths)
{
if (paths.empty())
return false;
for (const Preset &preset : printers.get_presets()) {
for (const auto &entry : paths)
if (preset.config.opt_string(entry.first) == entry.second)
return true;
}
return false;
}
} // namespace
bool append_printer_bed_assets(mz_zip_archive &zip, const DynamicPrintConfig &config, json &bundle_structure,
const std::string &printer_config_entry, int printer_index)
{
if (printer_config_entry.empty() || printer_index < 0)
return true;
json assets = json::object();
for (const BedAssetSlot &slot : k_bed_asset_slots) {
if (!config.has(slot.option))
continue;
const std::string stored = config.opt_string(slot.option);
if (stored.empty())
continue;
if (!utf8_is_file(stored)) {
BOOST_LOG_TRIVIAL(warning) << "Bed asset file is missing, skipping " << slot.option << ": " << stored;
continue;
}
const std::string ext = bed_asset_extension(stored, slot.option);
if (ext.empty()) {
BOOST_LOG_TRIVIAL(warning) << "Bed asset extension is not packable, skipping " << slot.option << ": " << stored;
continue;
}
boost::nowide::ifstream in(stored, std::ios::binary);
if (!in) {
BOOST_LOG_TRIVIAL(warning) << "Bed asset file cannot be read, skipping " << slot.option << ": " << stored;
continue;
}
const std::string bytes((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
if (in.bad()) {
BOOST_LOG_TRIVIAL(warning) << "Bed asset file cannot be read, skipping " << slot.option << ": " << stored;
continue;
}
const std::string zip_name = std::string("bed/") + std::to_string(printer_index) + "/" + slot.stem + ext;
const void *data = bytes.empty() ? static_cast<const void *>("") : static_cast<const void *>(bytes.data());
if (mz_zip_writer_add_mem(&zip, zip_name.c_str(), data, bytes.size(), MZ_DEFAULT_COMPRESSION) == MZ_FALSE) {
BOOST_LOG_TRIVIAL(error) << "Failed to add bed asset to printer bundle: " << zip_name;
return false;
}
assets[slot.option] = zip_name;
}
if (!assets.empty())
bundle_structure["printer_asset"][printer_config_entry] = std::move(assets);
return true;
}
// Import presets from UI control
PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string> & files,
std::function<int(std::string const &)> override_confirm,
@@ -1773,14 +2028,44 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string>
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " No bundle_structure.json found, importing presets into the user preset directory";
}
json bundle_json = json::object();
std::unordered_set<std::string> bed_asset_entries;
std::vector<PendingBedAsset> bed_assets;
std::map<std::string, std::map<std::string, std::string>> bed_paths_by_printer;
bool bed_paths_loaded = false;
if (has_bundle_structure && !bundle_base_dir.empty()) {
try {
boost::nowide::ifstream manifest_in(metadata_path.string());
manifest_in >> bundle_json;
} catch (const std::exception &err) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " failed to read bundle manifest: " << err.what();
bundle_json = json::object();
}
std::unordered_set<std::string> zip_entries;
const int zip_file_count = mz_zip_reader_get_num_files(&zip_archive);
for (int zi = 0; zi < zip_file_count; ++zi) {
mz_zip_archive_file_stat st;
if (mz_zip_reader_file_stat(&zip_archive, zi, &st))
zip_entries.insert(zip_slashes(st.m_filename));
}
bed_assets = pending_bed_assets(bundle_json, bundle_base_dir, bed_asset_entries, zip_entries);
for (PendingBedAsset &asset : bed_assets)
if (stage_bed_asset(zip_archive, asset, temp_folder))
bed_paths_by_printer[asset.printer_key].emplace(asset.option, asset.dest);
}
// Extract Files
int num_files = mz_zip_reader_get_num_files(&zip_archive);
const std::map<std::string, std::string> no_bed_paths;
for (int i = 0; i < num_files; i++) {
mz_zip_archive_file_stat file_stat;
status = mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
if (status) {
std::string file_name = file_stat.m_filename;
const std::string zip_entry = zip_slashes(file_name);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " From zip file: " << file << ". Read file name: " << file_stat.m_filename;
if (is_pending_bed_asset_entry(file_name, bed_asset_entries))
continue;
size_t index = file_name.find_last_of("/\\");
if (std::string::npos != index) {
file_name = file_name.substr(index + 1);
@@ -1798,13 +2083,26 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string>
if (MZ_FALSE == status) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Failed to open target file: " << target_file_path;
} else {
const auto paths_it = bed_paths_by_printer.find(zip_entry);
const std::map<std::string, std::string> &this_bed_paths =
paths_it != bed_paths_by_printer.end() ? paths_it->second : no_bed_paths;
bool is_success = import_json_presets(substitutions, target_file_path, override_confirm, rule, overwrite, result,
has_bundle_structure ? bundle_base_dir.string() : std::string());
has_bundle_structure ? bundle_base_dir.string() : std::string(),
this_bed_paths, &bed_paths_loaded);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " import target file: " << target_file_path << " import result" << is_success;
}
}
}
// Copy into bed/ only after this import loads a printer that still holds the staged path.
// A preset left from an earlier import must not count: declining this import leaves its files alone.
if (bed_paths_loaded) {
for (const PendingBedAsset &asset : bed_assets) {
if (printer_kept_bed_paths(printers, {{asset.option, asset.dest}}))
install_bed_asset(asset);
}
}
// Set imported_time to current time if not already set
if (metadata.imported_time == 0) {
metadata.imported_time = std::time(nullptr);
@@ -1846,7 +2144,9 @@ bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & s
ForwardCompatibilitySubstitutionRule rule,
int & overwrite,
std::vector<std::string> & result,
const std::string & bundle_dir)
const std::string & bundle_dir,
const std::map<std::string, std::string> & bed_asset_paths,
bool * bed_paths_loaded)
{
try {
DynamicPrintConfig config;
@@ -1910,6 +2210,10 @@ bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & s
if (inherit_preset)
inherits_value = inherit_preset->name; // keep the base_id redo below in sync
}
if (collection == &printers) {
for (const auto &entry : bed_asset_paths)
config.option<ConfigOptionString>(entry.first, true)->value = entry.second;
}
if (inherit_preset) {
new_config = inherit_preset->config;
new_config.apply(std::move(config));
@@ -1927,6 +2231,8 @@ bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & s
new_config.apply(std::move(config));
extend_default_config_length(new_config, true, default_preset.config);
}
if (bed_paths_loaded != nullptr && collection == &printers && !bed_asset_paths.empty())
*bed_paths_loaded = true;
Preset &preset = collection->load_preset(collection->path_from_name(name, inherit_preset == nullptr), preset_name, std::move(new_config), false);
preset.bundle_id = load_origin.bundle_id;
+3 -1
View File
@@ -319,7 +319,9 @@ public:
ForwardCompatibilitySubstitutionRule rule,
int& overwrite,
std::vector<std::string>& result,
const std::string& bundle_dir = "");
const std::string& bundle_dir = "",
const std::map<std::string, std::string> &bed_asset_paths = {},
bool *bed_paths_loaded = nullptr);
void save_user_presets(AppConfig& config, std::map<std::string, std::string>& need_to_delete_list);
void check_and_fix_user_presets_syncinfo(const std::string& user_id);
+2
View File
@@ -1148,6 +1148,8 @@ void PrintConfigDef::init_common_params()
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
// The URL is an example of the format to type, not a link, so it stays in the text.
// xgettext:no-url-check
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance. "
"Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL "
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "miniz_extension.hpp"
#include <miniz.h>
#include <nlohmann/json_fwd.hpp>
#include <string>
namespace Slic3r {
class DynamicPrintConfig;
// Not on PresetBundle.hpp: miniz.h's zlib-compat macros rename GUI methods (GLTexture::Compressor::compress).
// printer_config_entry is the zip name also pushed onto bundle_structure["printer_config"].
// printer_index is that entry's 0-based index in the exported printer_config list.
bool append_printer_bed_assets(mz_zip_archive &zip, const DynamicPrintConfig &config, nlohmann::json &bundle_structure,
const std::string &printer_config_entry, int printer_index);
} // namespace Slic3r
+2
View File
@@ -1,3 +1,5 @@
#pragma once
// paper: https://people.eecs.berkeley.edu/~jrs/meshpapers/GarlandHeckbert2.pdf
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
+32 -2
View File
@@ -65,6 +65,7 @@
#include <boost/nowide/cstdio.hpp>
#include <wx/valtext.h>
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PrinterBedAssets.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "MsgDialog.hpp"
@@ -3674,9 +3675,20 @@ ExportConfigsDialog::~ExportConfigsDialog()
}
void ExportConfigsDialog::on_dpi_changed(const wxRect &suggested_rect) {
if (m_include_bed_assets)
m_include_bed_assets->Rescale();
Layout();
}
void ExportConfigsDialog::update_include_bed_assets_enabled()
{
const bool on = get_curr_radio_type(m_export_type_btns) == m_exprot_type.preset_bundle;
if (m_include_bed_assets)
m_include_bed_assets->Enable(on);
if (m_include_bed_assets_label)
m_include_bed_assets_label->Enable(on);
}
void ExportConfigsDialog::show_export_result(const ExportCase &export_case)
{
MessageDialog *msg_dlg = nullptr;
@@ -3819,6 +3831,17 @@ wxBoxSizer *ExportConfigsDialog::create_export_config_item(wxWindow *parent)
static_export_printer_preset_bundle_text->SetFont(Label::Body_12);
static_export_printer_preset_bundle_text->SetForegroundColour(wxColour("#6B6B6B"));
radioBoxSizer->Add(static_export_printer_preset_bundle_text, 0, wxEXPAND | wxLEFT, FromDIP(22));
wxBoxSizer *include_bed_sizer = new wxBoxSizer(wxHORIZONTAL);
m_include_bed_assets = new ::CheckBox(parent);
m_include_bed_assets->SetValue(true);
include_bed_sizer->Add(m_include_bed_assets, 0, wxALIGN_CENTER_VERTICAL, 0);
m_include_bed_assets_label = new wxStaticText(parent, wxID_ANY, _L("Include custom bed texture and model"));
m_include_bed_assets_label->SetFont(Label::Body_12);
m_include_bed_assets_label->SetForegroundColour(wxColour("#6B6B6B"));
include_bed_sizer->Add(m_include_bed_assets_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5));
radioBoxSizer->Add(0, 0, 0, wxTOP, FromDIP(6));
radioBoxSizer->Add(include_bed_sizer, 0, wxEXPAND | wxLEFT, FromDIP(22));
update_include_bed_assets_enabled();
radioBoxSizer->Add(create_radio_item(m_exprot_type.filament_bundle, parent, wxEmptyString, m_export_type_btns), 0, wxEXPAND | wxTOP, FromDIP(10));
wxStaticText *static_export_filament_preset_bundle_text = new wxStaticText(parent, wxID_ANY, _L("User's filament preset set.\nCan be shared with others."),
wxDefaultPosition, wxDefaultSize);
@@ -3988,9 +4011,10 @@ void ExportConfigsDialog::select_curr_radiobox(std::vector<std::pair<RadioBox *,
radiobox_list[i].first->SetValue(false);
}
}
update_include_bed_assets_enabled();
}
ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_preset_bundle_to_file(const wxString &path)
ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_preset_bundle_to_file(const wxString &path, bool include_bed_assets)
{
std::string export_path = initial_file_path(path, "");
if (export_path.empty() || "initial_failed" == export_path) return ExportCase::EXPORT_CANCEL;
@@ -4092,6 +4116,12 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_preset_bundle_to_fi
bundle_structure["filament_config"] = filament_configs;
bundle_structure["process_config"] = process_configs;
if (include_bed_assets &&
!append_printer_bed_assets(zip_archive, printer_preset->config, bundle_structure, printer_config_file_name, 0)) {
mz_zip_writer_end(&zip_archive);
return ExportCase::ADD_FILE_FAIL;
}
std::string bundle_structure_str = bundle_structure.dump();
status = mz_zip_writer_add_mem(&zip_archive, BUNDLE_STRUCTURE_JSON_NAME, bundle_structure_str.data(), bundle_structure_str.size(), MZ_DEFAULT_COMPRESSION);
if (MZ_FALSE == status) {
@@ -4339,7 +4369,7 @@ wxWindow *ExportConfigsDialog::create_dialog_buttons(wxWindow* parent)
const wxString curr_radio_type = get_curr_radio_type(m_export_type_btns);
if (curr_radio_type == m_exprot_type.preset_bundle) {
export_case = archive_preset_bundle_to_file(path);
export_case = archive_preset_bundle_to_file(path, m_include_bed_assets->GetValue());
} else if (curr_radio_type == m_exprot_type.filament_bundle) {
export_case = archive_filament_bundle_to_file(path);
} else if (curr_radio_type == m_exprot_type.printer_preset) {
+4 -1
View File
@@ -271,6 +271,7 @@ protected:
private:
void data_init();
void select_curr_radiobox(std::vector<std::pair<RadioBox *, wxString>> &radiobox_list, int btn_idx);
void update_include_bed_assets_enabled();
void on_dpi_changed(const wxRect &suggested_rect) override;
void show_export_result(const ExportCase &export_case);
bool has_check_box_selected();
@@ -284,7 +285,7 @@ private:
int initial_zip_archive(mz_zip_archive &zip_archive, const std::string &file_path);
ExportCase save_zip_archive_to_file(mz_zip_archive &zip_archive);
ExportCase save_presets_to_zip(const std::string &export_file, const std::vector<std::pair<std::string, std::string>> &config_paths);
ExportCase archive_preset_bundle_to_file(const wxString &path);
ExportCase archive_preset_bundle_to_file(const wxString &path, bool include_bed_assets);
ExportCase archive_filament_bundle_to_file(const wxString &path);
ExportCase archive_printer_preset_to_file(const wxString &path);
ExportCase archive_filament_preset_to_file(const wxString &path);
@@ -304,6 +305,8 @@ private:
wxGridSizer * m_preset_sizer = nullptr;
wxPanel * m_presets_window = nullptr;
wxStaticText * m_serial_text = nullptr;
::CheckBox * m_include_bed_assets = nullptr;
wxStaticText * m_include_bed_assets_label = nullptr;
};
class CreatePresetForPrinterDialog : public DPIDialog
+15 -2
View File
@@ -379,14 +379,24 @@ void FilamentGroupPopup::OnTimer(wxTimerEvent &event)
// leaving event got triggered. So the timer is stopped first, then started
// again, causing the popup being dismissed immediately.
// To fix this, we check if cursor is still inside the popup window before
// dismissing.
// dismissing, and keep checking while it is: a leave event is not guaranteed
// to follow, and a popup left open keeps grabbing the mouse capture.
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
if (this->GetClientRect().Contains(pos)) {
StartTimer();
return;
}
#endif
Dismiss();
}
FilamentGroupPopup::~FilamentGroupPopup()
{
m_timer->Stop();
delete m_timer;
}
void FilamentGroupPopup::Dismiss() {
m_active = false;
PopupWindow::Dismiss();
@@ -405,7 +415,10 @@ void FilamentGroupPopup::OnEnterWindow(wxMouseEvent &)
// Ignore spurious ENTER synthesized by PopupWindow::OnMouseEvent2 on macOS.
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (!this->GetClientRect().Contains(pos)) return;
// On macOS the timer keeps running instead, and OnTimer closes the popup once the cursor has left it.
#ifndef __APPLE__
ResetTimer();
#endif
}
void FilamentGroupPopup::UpdateButtonStatus(int hover_idx)
+1
View File
@@ -27,6 +27,7 @@ class FilamentGroupPopup : public PopupWindow
{
public:
FilamentGroupPopup(wxWindow *parent);
~FilamentGroupPopup() override;
void tryPopup(Plater* plater,PartPlate* plate, bool slice_all);
void tryClose();
void Dismiss() override;
+9
View File
@@ -20,6 +20,8 @@
#include <wx/gdicmn.h>
#include <wx/dialog.h>
#include "slic3r/GUI/FilamentMapPanel.hpp"
#include "slic3r/GUI/FilamentGroupPopup.hpp"
#include "slic3r/GUI/MainFrame.hpp"
namespace Slic3r { namespace GUI {
@@ -377,6 +379,13 @@ FilamentMapMode FilamentMapDialog::get_mode()
int FilamentMapDialog::ShowModal()
{
// The Slice-plate hover popup is a transient popup that keeps grabbing the mouse
// capture while shown. Left behind this modal dialog it would starve the dialog of
// mouse events, so close it synchronously first, whichever path opened the dialog.
if (MainFrame* frame = wxGetApp().mainframe;
frame && frame->m_filament_group_popup && frame->m_filament_group_popup->IsShown())
frame->m_filament_group_popup->Dismiss();
update_panel_status(m_page_type);
return wxDialog::ShowModal();
}
+4 -3
View File
@@ -2654,9 +2654,10 @@ void GUI_App::init_webview_runtime()
BOOST_LOG_TRIVIAL(info) << "WebView2 runtime installed successfully.";
} else {
BOOST_LOG_TRIVIAL(error) << "WebView2 runtime installation failed or still not detected.";
wxMessageBox(_L("The Microsoft WebView2 Runtime could not be installed.\n"
"Some features, including the setup wizard, may appear blank until it is installed.\n"
"Please install it manually from https://developer.microsoft.com/microsoft-edge/webview2/ and restart Orca Slicer."),
wxMessageBox(format_wxstr(_L("The Microsoft WebView2 Runtime could not be installed.\n"
"Some features, including the setup wizard, may appear blank until it is installed.\n"
"Please install it manually from %1% and restart Orca Slicer."),
"https://developer.microsoft.com/microsoft-edge/webview2/"),
_L("WebView2 Runtime"), wxOK | wxICON_WARNING);
}
}
+2 -2
View File
@@ -235,7 +235,7 @@ void GridCellFilamentsEditor::SetSize(const wxRect& rect)
void GridCellFilamentsEditor::OnComboCloseUp(wxCommandEvent& evt)
{
wxGridCellChoiceEditor::OnComboCloseUp(evt);
wxGridCellChoiceEditor::DismissEditor();
}
void GridCellFilamentsEditor::BeginEdit(int row, int col, wxGrid* grid)
@@ -449,7 +449,7 @@ void GridCellChoiceEditor::SetSize(const wxRect &rect)
wxGridCellChoiceEditor::SetSize(rect);
}
void GridCellChoiceEditor::OnComboCloseUp(wxCommandEvent &evt) { wxGridCellChoiceEditor::OnComboCloseUp(evt); }
void GridCellChoiceEditor::OnComboCloseUp(wxCommandEvent &evt) { wxGridCellChoiceEditor::DismissEditor(); }
void GridCellChoiceEditor::BeginEdit(int row, int col, wxGrid *grid)
{
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#import <Cocoa/Cocoa.h>
@interface OtherInstanceMessageHandlerMac : NSObject
-6
View File
@@ -2309,12 +2309,6 @@ wxBoxSizer* MainFrame::create_side_tools()
bool slice = true;
// The Slice-plate hover popup is a transient popup that keeps grabbing
// the mouse capture while shown. Left behind the modal grouping dialog it
// would starve that dialog of mouse events, so close it synchronously first.
if (m_filament_group_popup)
m_filament_group_popup->Dismiss();
auto curr_plate = m_plater->get_partplate_list().get_curr_plate();
#ifdef __linux__
PresetBundle* preset = wxGetApp().preset_bundle;
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#include "slic3r/GUI/wxExtensions.hpp"
#include <map>
#include <cstdint>
+2 -2
View File
@@ -12820,8 +12820,8 @@ void Plater::priv::on_select_bed_type(wxCommandEvent &evt)
// update plater with new config
q->on_config_change(wxGetApp().preset_bundle->full_config());
if (auto *plate_tab = dynamic_cast<TabPrintPlate *>(wxGetApp().get_plate_tab()))
plate_tab->update_model_config();
// Rebuild the plate tab from the current selection; its cached plates may be gone after a reset.
wxGetApp().obj_list()->update_and_show_object_settings_item();
// update app_config
AppConfig* app_config = wxGetApp().app_config;
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#import <Cocoa/Cocoa.h>
@interface RemovableDriveManagerMM : NSObject
+1 -1
View File
@@ -5957,7 +5957,7 @@ if (is_marlin_flavor)
{
// In the configuration group rather than a group of its own: a full-width widget line
// records no mode, so a group holding only this line would show in every mode.
auto line = Line{ L("Modes"), L("") };
auto line = Line{ L("Modes"), "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) -> wxSizer* {
int n_cols = 0, n_rows = 0, layout = 0;
+15
View File
@@ -13,6 +13,10 @@
#include <wx/gdicmn.h>
#include <wx/webview.h>
#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif
namespace Slic3r { namespace GUI {
WebDialog::WebDialog(wxWindow* parent,
@@ -52,6 +56,17 @@ WebDialog::WebDialog(wxWindow* parent,
Bind(wxEVT_CLOSE_WINDOW, &WebDialog::on_close_window, this);
}
void WebDialog::use_normal_window_type()
{
#ifdef __WXGTK__
// wxGTK types every wxDialog as a dialog, and Mutter offers no maximize for anything but a
// normal window. Mutter also stops hiding a normal window from the taskbar, so keep it hidden
// as a dialog with a parent is everywhere else.
gtk_window_set_type_hint(GTK_WINDOW(m_widget), GDK_WINDOW_TYPE_HINT_NORMAL);
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
#endif
}
void WebDialog::add_user_scripts()
{
if (wxWebView* wv = browser()) {
+4
View File
@@ -62,6 +62,10 @@ public:
bool is_open() const { return m_open; }
// Lets a modeless window be maximized on X11; call before Show(). Modal ones stay dialogs so the
// window manager keeps routing focus from the parent to them.
void use_normal_window_type();
// The payload submitted via window.orca.submit() (modal use), if any.
const std::optional<nlohmann::json>& result() const { return m_result; }
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#include <wx/textctrl.h>
#include <wx/colour.h>
+1
View File
@@ -197,6 +197,7 @@ bool CloudPluginService::download_cloud_plugin(PluginDescriptor& entry,
Http http = Http::get(download_url);
http.timeout_connect(30)
.timeout_max(300)
.tls_verify(true)
.on_complete([&body, &http_status](std::string response_body, unsigned status) {
body = std::move(response_body);
http_status = status;
+1
View File
@@ -399,6 +399,7 @@ py::object ui_create_window(const std::string& html, const std::string& title, i
if (UiRegistry::instance().is_open(new_id))
dlg->Destroy();
} else {
dlg->use_normal_window_type();
dlg->Show();
}
});
@@ -6,6 +6,8 @@
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/fstream.hpp>
#include <cstddef>
#include <fstream>
@@ -22,6 +24,7 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/PrinterBedAssets.hpp"
#include "test_utils.hpp"
@@ -5853,6 +5856,609 @@ TEST_CASE("Config import confines zip entries, preset names and bundle ids to th
}
}
namespace {
const std::string kTextureBytes("\x89PNG\x00tex", 8);
const std::string kModelBytes("stl\x00model", 9);
const std::string kOtherBytes("\x89PNG\x00other", 10);
void write_bytes(const std::string &utf8_path, const std::string &bytes)
{
const auto slash = utf8_path.find_last_of("/\\");
if (slash != std::string::npos) {
#ifdef _WIN32
fs::create_directories(fs::path(boost::nowide::widen(utf8_path.substr(0, slash))));
#else
fs::create_directories(utf8_path.substr(0, slash));
#endif
}
boost::nowide::ofstream out(utf8_path, std::ios::binary | std::ios::trunc);
REQUIRE(out);
out.write(bytes.data(), static_cast<std::streamsize>(bytes.size()));
REQUIRE(out);
}
fs::path write_printer_json(const DynamicPrintConfig &defaults, const fs::path &file, const std::string &name,
const std::string &texture, const std::string &model, const std::string &inherits = {})
{
DynamicPrintConfig config(defaults);
config.option<ConfigOptionString>("printer_settings_id", true)->value = name;
config.option<ConfigOptionString>("inherits", true)->value = inherits;
if (texture.empty())
config.erase("bed_custom_texture");
else
config.option<ConfigOptionString>("bed_custom_texture", true)->value = texture;
if (model.empty())
config.erase("bed_custom_model");
else
config.option<ConfigOptionString>("bed_custom_model", true)->value = model;
fs::create_directories(file.parent_path());
config.save_to_json(file.string(), name, "User", "1.0.0");
return file;
}
nlohmann::json bed_manifest(const std::string &bundle_id)
{
nlohmann::json structure = nlohmann::json::object();
structure["id"] = bundle_id;
structure["bundle_type"] = "printer config bundle";
structure["printer_config"] = nlohmann::json::array({"printer/BedPrinter.json"});
return structure;
}
void set_printer_bed_asset(nlohmann::json &structure, const std::string &option, const std::string &zip_name,
const std::string &printer_entry = "printer/BedPrinter.json")
{
structure["printer_asset"][printer_entry][option] = zip_name;
}
nlohmann::json pack_bed_zip(const fs::path &zip_file, const fs::path &printer_json, const DynamicPrintConfig &defaults,
const std::string &texture, const std::string &model, const std::string &bundle_id,
bool include_bed_assets = true)
{
DynamicPrintConfig config(defaults);
if (!texture.empty())
config.option<ConfigOptionString>("bed_custom_texture", true)->value = texture;
if (!model.empty())
config.option<ConfigOptionString>("bed_custom_model", true)->value = model;
nlohmann::json structure = bed_manifest(bundle_id);
mz_zip_archive zip;
mz_zip_zero_struct(&zip);
const std::string printer_bytes = read_file(printer_json);
REQUIRE(open_zip_writer(&zip, zip_file.string()));
REQUIRE(mz_zip_writer_add_mem(&zip, "printer/BedPrinter.json", printer_bytes.data(), printer_bytes.size(),
MZ_DEFAULT_COMPRESSION));
if (include_bed_assets)
REQUIRE(append_printer_bed_assets(zip, config, structure, "printer/BedPrinter.json", 0));
const std::string manifest = structure.dump();
REQUIRE(mz_zip_writer_add_mem(&zip, BUNDLE_STRUCTURE_JSON_NAME, manifest.data(), manifest.size(),
MZ_DEFAULT_COMPRESSION));
REQUIRE(mz_zip_writer_finalize_archive(&zip));
REQUIRE(close_zip_writer(&zip));
return structure;
}
std::vector<std::string> zip_entry_names(const fs::path &zip_file)
{
mz_zip_archive zip;
mz_zip_zero_struct(&zip);
REQUIRE(open_zip_reader(&zip, zip_file.string()));
std::vector<std::string> names;
const int n = static_cast<int>(mz_zip_reader_get_num_files(&zip));
for (int i = 0; i < n; ++i) {
mz_zip_archive_file_stat st;
REQUIRE(mz_zip_reader_file_stat(&zip, i, &st));
names.emplace_back(st.m_filename);
}
REQUIRE(close_zip_reader(&zip));
return names;
}
const Preset *import_named_printer(PresetBundle &bundle, const fs::path &zip_file, const fs::path &data_root,
const std::string &bare_name, int confirm = 1)
{
ScopedDataDir scoped(data_root);
AppConfig config;
std::vector<std::string> files{zip_file.string()};
bundle.import_presets(files, [confirm](std::string const &) { return confirm; },
ForwardCompatibilitySubstitutionRule::Disable, config);
for (const Preset &preset : bundle.printers.get_presets())
if (get_preset_bare_name(preset.name) == bare_name && !preset.file.empty())
return &preset;
return nullptr;
}
const Preset *import_bed(PresetBundle &bundle, const fs::path &zip_file, const fs::path &data_root, int confirm = 1)
{
return import_named_printer(bundle, zip_file, data_root, "BedPrinter", confirm);
}
struct BedRoots {
ScopedTemporaryDir temp_dir;
fs::path src;
fs::path data_root;
PresetBundle bundle;
BedRoots()
: src(temp_dir.path() / "src")
, data_root(temp_dir.path() / "datadir")
{}
};
} // namespace
TEST_CASE("Printer bundle round trip packs bed texture and model and rewrites both keys", "[Preset][Bundle]")
{
// tex_rel "*" builds a non-ASCII source path under the temp directory.
const auto pack_import = [](const std::string &bundle_id, const std::string &tex_rel, const std::string &mod_rel,
const std::string &tex_bytes, const std::string &mod_bytes, bool tex_exists,
bool mod_exists, int imports) {
ScopedTemporaryDir temp_dir;
const fs::path src = temp_dir.path() / "src";
const fs::path data_root = temp_dir.path() / "datadir";
std::string texture = tex_rel.empty() ? std::string() : (src / tex_rel).make_preferred().string();
std::string model = mod_rel.empty() ? std::string() : (src / mod_rel).make_preferred().string();
if (tex_rel == "*") {
const fs::path dir = fs::path(src.wstring()) / fs::path(boost::nowide::widen("\xE5\xBA\x8A"));
texture = boost::nowide::narrow((dir / fs::path(boost::nowide::widen("\xE7\xBA\xB9\xE7\x90\x86.png"))).wstring());
}
if (tex_exists)
write_bytes(texture, tex_bytes);
if (mod_exists)
write_bytes(model, mod_bytes);
PresetBundle bundle;
const fs::path printer_json = write_printer_json(bundle.printers.default_preset().config, src / "BedPrinter.json",
"BedPrinter", texture, model);
const fs::path zip_file = src / "bundle.orca_printer";
const nlohmann::json structure = pack_bed_zip(zip_file, printer_json, bundle.printers.default_preset().config,
texture, model, bundle_id);
const Preset *saved = nullptr;
for (int i = 0; i < imports; ++i)
saved = import_bed(bundle, zip_file, data_root);
REQUIRE(saved != nullptr);
const std::string got[] = {saved->config.opt_string("bed_custom_texture"), saved->config.opt_string("bed_custom_model")};
const std::string rel[] = {tex_rel, mod_rel};
const std::string bytes[] = {tex_bytes, mod_bytes};
const std::string path[] = {texture, model};
const bool exists[] = {tex_exists, mod_exists};
const char *option[] = {"bed_custom_texture", "bed_custom_model"};
const char *stem[] = {"texture", "model"};
for (int slot = 0; slot < 2; ++slot) {
if (exists[slot]) {
const std::string ext = fs::path(rel[slot] == "*" ? "x.png" : rel[slot]).extension().string();
const std::string zip_name = std::string("bed/0/") + stem[slot] + ext;
REQUIRE(structure["printer_asset"]["printer/BedPrinter.json"][option[slot]] == zip_name);
REQUIRE(fs::path(got[slot]).filename().string() == std::string(stem[slot]) + fs::path(zip_name).extension().string());
REQUIRE(fs::path(got[slot]).parent_path().filename() == "0");
REQUIRE(fs::path(got[slot]).parent_path().parent_path().filename() == "bed");
REQUIRE(got[slot].find(bundle_id) != std::string::npos);
REQUIRE(read_file(got[slot]) == bytes[slot]);
REQUIRE(got[slot] != path[slot]);
} else if (!rel[slot].empty() && rel[slot] != "*") {
REQUIRE_FALSE(structure.value("printer_asset", nlohmann::json::object())
.value("printer/BedPrinter.json", nlohmann::json::object())
.contains(option[slot]));
REQUIRE(got[slot] == path[slot]);
} else if (rel[slot].empty()) {
REQUIRE(got[slot].empty());
}
}
if (imports > 1) {
int copies = 0;
for (fs::recursive_directory_iterator it(data_root), end; it != end; ++it)
if (it->path().filename() == "texture.png" || it->path().filename() == "model.stl")
++copies;
REQUIRE(copies == (tex_exists ? 1 : 0) + (mod_exists ? 1 : 0));
}
};
SECTION("both assets are packed from the preset config and rewritten on import") {
pack_import("bed-roundtrip", "fixture/baseline_bed_texture.png", "fixture/baseline_bed_model.stl",
kTextureBytes, kModelBytes, true, true, 1);
}
SECTION("texture only") {
pack_import("texture-only", "only.png", "", kTextureBytes, "", true, false, 1);
}
SECTION("model only") {
pack_import("model-only", "", "only.stl", "", kModelBytes, false, true, 1);
}
SECTION("texture and model share a source basename") {
pack_import("shared-basename", "a/plate.png", "b/plate.stl", kTextureBytes, kOtherBytes, true, true, 1);
}
SECTION("importing the same bundle twice keeps one copy of the assets") {
// First import installs kTextureBytes / kModelBytes. The second pack uses different bytes so
// the Yes-overwrite path (copy_file overwrite_existing) is actually proven.
BedRoots bed;
const std::string texture = (bed.src / "twice.png").make_preferred().string();
const std::string model = (bed.src / "twice.stl").make_preferred().string();
write_bytes(texture, kTextureBytes);
write_bytes(model, kModelBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config,
bed.src / "BedPrinter.json", "BedPrinter", texture, model);
const fs::path zip_file = bed.src / "bundle.orca_printer";
pack_bed_zip(zip_file, printer_json, bed.bundle.printers.default_preset().config, texture, model, "reimport");
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
const std::string got_tex = saved->config.opt_string("bed_custom_texture");
const std::string got_mod = saved->config.opt_string("bed_custom_model");
REQUIRE(read_file(got_tex) == kTextureBytes);
REQUIRE(read_file(got_mod) == kModelBytes);
write_bytes(texture, kOtherBytes);
write_bytes(model, std::string("stl\x00other", 9));
pack_bed_zip(zip_file, printer_json, bed.bundle.printers.default_preset().config, texture, model, "reimport");
saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == got_tex);
REQUIRE(saved->config.opt_string("bed_custom_model") == got_mod);
REQUIRE(read_file(got_tex) == kOtherBytes);
REQUIRE(read_file(got_mod) == std::string("stl\x00other", 9));
}
SECTION("a non-ASCII source path is packed") {
pack_import("non-ascii", "*", "", kTextureBytes, "", true, false, 1);
}
}
TEST_CASE("An old printer bundle without printer_asset imports unchanged", "[Preset][Bundle]")
{
BedRoots bed;
const std::string texture = (bed.src / "old.png").make_preferred().string();
const std::string model = (bed.src / "old.stl").make_preferred().string();
write_bytes(texture, kTextureBytes);
write_bytes(model, kModelBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", texture, model);
nlohmann::json structure = bed_manifest("old-bundle");
REQUIRE_FALSE(structure.contains("printer_asset"));
const fs::path zip_file = bed.src / "old.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()}});
// A bed/ folder left by an earlier import into the same bundle folder must survive an asset-less import.
const fs::path old_bed = bed.data_root / "user" / "default" / "_local" / "old-bundle" / "bed";
write_bytes((old_bed / "texture.png").string(), kTextureBytes);
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
REQUIRE(saved->config.opt_string("bed_custom_model") == model);
CHECK(fs::exists(old_bed / "texture.png"));
}
TEST_CASE("Printer bundle asset names that escape the bundle directory are rejected", "[Preset][Bundle]")
{
const char *bad_names[] = {"../../pwned.png", "bed/../../pwned.png", "..\\..\\pwned.png", "C:/Windows/pwned.png",
"machine/texture.png", "/bed/texture.png"};
const std::string marker = "PWNED-BED-ASSET-MARKER";
for (const char *name : bad_names) {
DYNAMIC_SECTION("rejects " << name) {
BedRoots bed;
const std::string texture = (bed.src / "kept.png").make_preferred().string();
const std::string model = (bed.src / "kept.stl").make_preferred().string();
write_bytes(texture, kTextureBytes);
write_bytes(model, kModelBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config,
bed.src / "BedPrinter.json", "BedPrinter", texture, model);
nlohmann::json structure = bed_manifest("traverse");
set_printer_bed_asset(structure, "bed_custom_texture", name);
std::vector<std::pair<std::string, std::string>> entries = {
{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()}};
if (name[0] != '/' && name[0] != '\\')
entries.emplace_back(name, marker);
const fs::path zip_file = bed.src / "bad.orca_printer";
write_zip(zip_file, entries);
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
REQUIRE(saved->config.opt_string("bed_custom_model") == model);
CHECK_FALSE(any_filename_contains(bed.data_root, fs::path(name).filename().string()));
if (fs::exists(bed.data_root))
for (fs::recursive_directory_iterator it(bed.data_root), end; it != end; ++it)
if (fs::is_regular_file(*it))
CHECK(read_file(*it).find(marker) == std::string::npos);
}
}
}
TEST_CASE("A printer bundle asset that is missing is skipped", "[Preset][Bundle]")
{
SECTION("a manifest entry with no zip bytes leaves the original path") {
BedRoots bed;
const std::string texture = (bed.src / "gone.png").make_preferred().string();
write_bytes(texture, kTextureBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", texture, {});
nlohmann::json structure = bed_manifest("missing-zip");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.png");
const fs::path zip_file = bed.src / "missing.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()}});
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
CHECK_FALSE(any_filename_contains(bed.data_root, "texture.png"));
}
SECTION("a missing file at export is omitted and the export still succeeds") {
BedRoots bed;
const std::string missing = (bed.src / "missing.png").make_preferred().string();
const std::string model = (bed.src / "present.stl").make_preferred().string();
write_bytes(model, kModelBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", missing, model);
const fs::path zip_file = bed.src / "partial.orca_printer";
const nlohmann::json structure = pack_bed_zip(zip_file, printer_json, bed.bundle.printers.default_preset().config,
missing, model, "missing-export");
REQUIRE_FALSE(structure["printer_asset"]["printer/BedPrinter.json"].contains("bed_custom_texture"));
REQUIRE(structure["printer_asset"]["printer/BedPrinter.json"]["bed_custom_model"] == "bed/0/model.stl");
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == missing);
REQUIRE(read_file(saved->config.opt_string("bed_custom_model")) == kModelBytes);
REQUIRE(fs::path(saved->config.opt_string("bed_custom_model")).filename() == "model.stl");
CHECK_FALSE(any_filename_contains(bed.data_root, "texture.png"));
}
}
TEST_CASE("An inherited printer bundle rewrites bed paths taken from the parent", "[Preset][Bundle]")
{
BedRoots bed;
const std::string texture = (bed.src / "parent.png").make_preferred().string();
const std::string model = (bed.src / "parent.stl").make_preferred().string();
write_bytes(texture, kTextureBytes);
write_bytes(model, kModelBytes);
DynamicPrintConfig parent(bed.bundle.printers.default_preset().config);
parent.option<ConfigOptionString>("printer_settings_id", true)->value = "ParentPrinter";
parent.option<ConfigOptionString>("inherits", true)->value.clear();
parent.option<ConfigOptionString>("bed_custom_texture", true)->value = texture;
parent.option<ConfigOptionString>("bed_custom_model", true)->value = model;
bed.bundle.printers.load_preset((bed.src / "ParentPrinter.json").string(), "ParentPrinter", std::move(parent), false);
const fs::path child_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", {}, {}, "ParentPrinter");
const nlohmann::json written = nlohmann::json::parse(read_file(child_json));
REQUIRE_FALSE(written.contains("bed_custom_texture"));
REQUIRE_FALSE(written.contains("bed_custom_model"));
nlohmann::json structure = bed_manifest("inherit-bed");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.png");
set_printer_bed_asset(structure, "bed_custom_model", "bed/0/model.stl");
const fs::path zip_file = bed.src / "inherit.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(child_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{"bed/0/texture.png", kTextureBytes}, {"bed/0/model.stl", kModelBytes}});
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") != texture);
REQUIRE(saved->config.opt_string("bed_custom_model") != model);
REQUIRE(read_file(saved->config.opt_string("bed_custom_texture")) == kTextureBytes);
REQUIRE(read_file(saved->config.opt_string("bed_custom_model")) == kModelBytes);
REQUIRE(fs::path(saved->config.opt_string("bed_custom_texture")).filename() == "texture.png");
REQUIRE(fs::path(saved->config.opt_string("bed_custom_model")).filename() == "model.stl");
}
TEST_CASE("A printer bundle rejects bed assets whose extension the bed dialog does not allow", "[Preset][Bundle]")
{
BedRoots bed;
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", {}, {});
nlohmann::json structure = bed_manifest("bad-ext");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.stl");
set_printer_bed_asset(structure, "bed_custom_model", "bed/0/model.exe");
const fs::path zip_file = bed.src / "bad-ext.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{"bed/0/texture.stl", kTextureBytes}, {"bed/0/model.exe", std::string("MZ")}});
REQUIRE(import_bed(bed.bundle, zip_file, bed.data_root) != nullptr);
CHECK_FALSE(any_filename_contains(bed.data_root, "model.exe"));
CHECK_FALSE(any_filename_contains(bed.data_root, "texture.stl"));
}
TEST_CASE("A manifest asset name that points at a preset does not drop that preset", "[Preset][Bundle]")
{
BedRoots bed;
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", {}, {});
nlohmann::json structure = bed_manifest("name-clash");
set_printer_bed_asset(structure, "bed_custom_texture", "printer/BedPrinter.json");
const fs::path zip_file = bed.src / "clash.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()}});
REQUIRE(import_bed(bed.bundle, zip_file, bed.data_root) != nullptr);
CHECK_FALSE(any_filename_contains(bed.data_root, "texture.png"));
}
TEST_CASE("A declined printer bundle import leaves existing bed files alone", "[Preset][Bundle]")
{
BedRoots bed;
const fs::path kept = bed.data_root / "user" / "default" / "_local" / "keep-bed" / "bed" / "texture.png";
const std::string original("kept-texture");
write_bytes(kept.string(), original);
DynamicPrintConfig existing(bed.bundle.printers.default_preset().config);
existing.option<ConfigOptionString>("printer_settings_id", true)->value = "BedPrinter";
existing.option<ConfigOptionString>("inherits", true)->value.clear();
bed.bundle.printers.load_preset((bed.src / "existing.json").string(), "_local/keep-bed/BedPrinter", std::move(existing),
false);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", {}, {});
nlohmann::json structure = bed_manifest("keep-bed");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.png");
set_printer_bed_asset(structure, "bed_custom_model", "bed/0/model.stl");
const fs::path zip_file = bed.src / "keep.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{"bed/0/texture.png", kTextureBytes}, {"bed/0/model.stl", kModelBytes}});
import_bed(bed.bundle, zip_file, bed.data_root, 0);
// This import wrote nothing: the existing texture is unchanged and model.stl was not added.
REQUIRE(fs::exists(kept));
REQUIRE(read_file(kept) == original);
CHECK_FALSE(fs::exists(kept.parent_path() / "model.stl"));
CHECK_FALSE(fs::exists(kept.parent_path() / "0" / "model.stl"));
}
TEST_CASE("A declined re-import does not replace bed bytes an earlier import wrote", "[Preset][Bundle]")
{
BedRoots bed;
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", {}, {});
nlohmann::json structure = bed_manifest("reimport-no");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.png");
const std::string manifest = structure.dump();
const std::string printer_bytes = read_file(printer_json);
const fs::path first_zip = bed.src / "first.orca_printer";
const fs::path second_zip = bed.src / "second.orca_printer";
write_zip(first_zip, {{"printer/BedPrinter.json", printer_bytes}, {BUNDLE_STRUCTURE_JSON_NAME, manifest},
{"bed/0/texture.png", kTextureBytes}});
write_zip(second_zip, {{"printer/BedPrinter.json", printer_bytes}, {BUNDLE_STRUCTURE_JSON_NAME, manifest},
{"bed/0/texture.png", kOtherBytes}});
const Preset *saved = import_bed(bed.bundle, first_zip, bed.data_root);
REQUIRE(saved != nullptr);
const std::string installed = saved->config.opt_string("bed_custom_texture");
REQUIRE(read_file(installed) == kTextureBytes);
import_bed(bed.bundle, second_zip, bed.data_root, 0);
REQUIRE(fs::exists(installed));
REQUIRE(read_file(installed) == kTextureBytes);
}
TEST_CASE("Export with bed assets disabled packs no bed files and writes no printer_asset", "[Preset][Bundle]")
{
BedRoots bed;
const std::string texture = (bed.src / "off.png").make_preferred().string();
const std::string model = (bed.src / "off.stl").make_preferred().string();
write_bytes(texture, kTextureBytes);
write_bytes(model, kModelBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", texture, model);
const fs::path zip_file = bed.src / "off.orca_printer";
const nlohmann::json structure = pack_bed_zip(zip_file, printer_json, bed.bundle.printers.default_preset().config,
texture, model, "bed-off", false);
REQUIRE_FALSE(structure.contains("printer_asset"));
for (const std::string &name : zip_entry_names(zip_file))
REQUIRE(name.compare(0, 4, "bed/") != 0);
}
TEST_CASE("Two printers in one bundle keep separate bed folders", "[Preset][Bundle]")
{
BedRoots bed;
const std::string tex_a = (bed.src / "a.png").make_preferred().string();
const std::string mod_a = (bed.src / "a.stl").make_preferred().string();
const std::string tex_b = (bed.src / "b.png").make_preferred().string();
const std::string mod_b = (bed.src / "b.stl").make_preferred().string();
write_bytes(tex_a, kTextureBytes);
write_bytes(mod_a, kModelBytes);
write_bytes(tex_b, kOtherBytes);
write_bytes(mod_b, std::string("stl\x00other", 9));
const fs::path json_a = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "PrinterA.json",
"PrinterA", tex_a, mod_a);
const fs::path json_b = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "PrinterB.json",
"PrinterB", tex_b, mod_b);
nlohmann::json structure = nlohmann::json::object();
structure["id"] = "two-printers";
structure["bundle_type"] = "printer config bundle";
structure["printer_config"] = nlohmann::json::array({"printer/PrinterA.json", "printer/PrinterB.json"});
DynamicPrintConfig config_a(bed.bundle.printers.default_preset().config);
config_a.option<ConfigOptionString>("bed_custom_texture", true)->value = tex_a;
config_a.option<ConfigOptionString>("bed_custom_model", true)->value = mod_a;
DynamicPrintConfig config_b(bed.bundle.printers.default_preset().config);
config_b.option<ConfigOptionString>("bed_custom_texture", true)->value = tex_b;
config_b.option<ConfigOptionString>("bed_custom_model", true)->value = mod_b;
const fs::path zip_file = bed.src / "two.orca_printer";
mz_zip_archive zip;
mz_zip_zero_struct(&zip);
const std::string bytes_a = read_file(json_a);
const std::string bytes_b = read_file(json_b);
REQUIRE(open_zip_writer(&zip, zip_file.string()));
REQUIRE(mz_zip_writer_add_mem(&zip, "printer/PrinterA.json", bytes_a.data(), bytes_a.size(), MZ_DEFAULT_COMPRESSION));
REQUIRE(mz_zip_writer_add_mem(&zip, "printer/PrinterB.json", bytes_b.data(), bytes_b.size(), MZ_DEFAULT_COMPRESSION));
REQUIRE(append_printer_bed_assets(zip, config_a, structure, "printer/PrinterA.json", 0));
REQUIRE(append_printer_bed_assets(zip, config_b, structure, "printer/PrinterB.json", 1));
const std::string manifest = structure.dump();
REQUIRE(mz_zip_writer_add_mem(&zip, BUNDLE_STRUCTURE_JSON_NAME, manifest.data(), manifest.size(),
MZ_DEFAULT_COMPRESSION));
REQUIRE(mz_zip_writer_finalize_archive(&zip));
REQUIRE(close_zip_writer(&zip));
REQUIRE(structure["printer_asset"]["printer/PrinterA.json"]["bed_custom_texture"] == "bed/0/texture.png");
REQUIRE(structure["printer_asset"]["printer/PrinterB.json"]["bed_custom_texture"] == "bed/1/texture.png");
ScopedDataDir scoped(bed.data_root);
AppConfig app_config;
std::vector<std::string> files{zip_file.string()};
bed.bundle.import_presets(files, [](std::string const &) { return 1; },
ForwardCompatibilitySubstitutionRule::Disable, app_config);
const Preset *saved_a = nullptr;
const Preset *saved_b = nullptr;
for (const Preset &preset : bed.bundle.printers.get_presets()) {
if (preset.file.empty())
continue;
if (get_preset_bare_name(preset.name) == "PrinterA")
saved_a = &preset;
if (get_preset_bare_name(preset.name) == "PrinterB")
saved_b = &preset;
}
REQUIRE(saved_a != nullptr);
REQUIRE(saved_b != nullptr);
const std::string got_tex_a = saved_a->config.opt_string("bed_custom_texture");
const std::string got_tex_b = saved_b->config.opt_string("bed_custom_texture");
REQUIRE(fs::path(got_tex_a).parent_path().filename() == "0");
REQUIRE(fs::path(got_tex_b).parent_path().filename() == "1");
REQUIRE(got_tex_a != got_tex_b);
REQUIRE(read_file(got_tex_a) == kTextureBytes);
REQUIRE(read_file(got_tex_b) == kOtherBytes);
REQUIRE(read_file(saved_a->config.opt_string("bed_custom_model")) == kModelBytes);
REQUIRE(read_file(saved_b->config.opt_string("bed_custom_model")) == std::string("stl\x00other", 9));
}
TEST_CASE("A printer_asset entry keyed to a printer not in the zip is ignored", "[Preset][Bundle]")
{
BedRoots bed;
const std::string texture = (bed.src / "kept.png").make_preferred().string();
write_bytes(texture, kTextureBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", texture, {});
nlohmann::json structure = bed_manifest("ghost-key");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/texture.png", "printer/Ghost.json");
const fs::path zip_file = bed.src / "ghost.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{"bed/0/texture.png", kOtherBytes}});
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
CHECK_FALSE(any_filename_contains(bed.data_root, "texture.png"));
}
TEST_CASE("Keyed printer_asset rejects malformed bed paths", "[Preset][Bundle]")
{
const char *bad_names[] = {"bed/texture.png", "bed/x/texture.png", "bed/0/../texture.png", "bed/0/model.png"};
const std::string marker = "PWNED-BED-ASSET-MARKER";
for (const char *name : bad_names) {
DYNAMIC_SECTION("rejects " << name) {
BedRoots bed;
const std::string texture = (bed.src / "kept.png").make_preferred().string();
write_bytes(texture, kTextureBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config,
bed.src / "BedPrinter.json", "BedPrinter", texture, {});
nlohmann::json structure = bed_manifest("bad-keyed");
set_printer_bed_asset(structure, "bed_custom_texture", name);
std::vector<std::pair<std::string, std::string>> entries = {
{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{name, marker}};
const fs::path zip_file = bed.src / "bad-keyed.orca_printer";
write_zip(zip_file, entries);
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
if (fs::exists(bed.data_root))
for (fs::recursive_directory_iterator it(bed.data_root), end; it != end; ++it)
if (fs::is_regular_file(*it))
CHECK(read_file(*it).find(marker) == std::string::npos);
}
}
SECTION("field name does not match file kind") {
BedRoots bed;
const std::string texture = (bed.src / "kept.png").make_preferred().string();
write_bytes(texture, kTextureBytes);
const fs::path printer_json = write_printer_json(bed.bundle.printers.default_preset().config, bed.src / "BedPrinter.json",
"BedPrinter", texture, {});
nlohmann::json structure = bed_manifest("kind-mismatch");
set_printer_bed_asset(structure, "bed_custom_texture", "bed/0/model.stl");
const fs::path zip_file = bed.src / "kind.orca_printer";
write_zip(zip_file, {{"printer/BedPrinter.json", read_file(printer_json)}, {BUNDLE_STRUCTURE_JSON_NAME, structure.dump()},
{"bed/0/model.stl", kModelBytes}});
const Preset *saved = import_bed(bed.bundle, zip_file, bed.data_root);
REQUIRE(saved != nullptr);
REQUIRE(saved->config.opt_string("bed_custom_texture") == texture);
CHECK_FALSE(any_filename_contains(bed.data_root, "model.stl"));
}
}
TEST_CASE("A saved printer preset reloads the tool layout it was saved with", "[Preset][Bundle][IMEX]")
{
ScopedTemporaryDir temp_dir;