Compare commits

...

19 Commits

Author SHA1 Message Date
Ian Chua
f179df525d remove mesa override 2026-05-20 10:33:54 +08:00
SoftFever
46e47cec0a Fix ObjectTable crash on cell deselect after wxWidgets 3.3.2 upgrade (#13740)
GridCellSupportEditor::DoActivate dereferenced
grid->GetSelectedBlocks().begin() without checking against end(). In
wxWidgets 3.1.5 that range always contained at least one block; in 3.3.2
it is empty after the user deselects, and the dereference crashes inside
wxGridBlockCoords::GetLeftCol().

Fall back to the (row, col) that triggered activation when the selection
is empty, so the existing single-cell branch handles it. While here,
drop a dead local_table cast that was never read.
2026-05-19 18:14:33 +08:00
Ioannis Giannakas
f4a9830752 Fix reverse on even hint (#13739) 2026-05-19 10:50:26 +01:00
Brandon Wees
1f2ed70288 fix: macOS deep links after wxWidgets 3.3.2 upgrade (#13737)
Fix macOS orcaslicer:// deep links after wxWidgets 3.3.2 upgrade

Install an OrcaSlicer-owned kAEGetURL Apple Event handler from
on_init_inner(). The wxWidgets 3.3.2 handler registered in
applicationWillFinishLaunching: stopped delivering URL events to
GUI_App::MacOpenURL on macOS (#13119), so links from Printables /
Thingiverse opened a blank project instead of importing the model.

Registering our own handler late in startup is last-writer-wins on
NSAppleEventManager and routes back to the existing MacOpenURL ->
start_download path, restoring the pre-upgrade behavior without
touching wxWidgets.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 17:13:07 +08:00
Ioannis Giannakas
3370e224b2 Cooling: add per-printer non-zero fan PWM floor and fix N=0 fan ramp override (#13715)
* Cooling: add per-printer non-zero fan PWM floor and fix N=0 ramp override
* updated parameter naming
2026-05-19 09:40:53 +01:00
SoftFever
7fc1e109c5 delete misshaped profiles 2026-05-19 14:35:20 +08:00
SoftFever
374e4ad8a9 Follow up update for PR #13724: improve GC and shorten comments 2026-05-19 14:32:18 +08:00
SoftFever
d0fbda3af1 Fix Linux segfault when loading Fluidd/Mainsail printer hosts (#7210) (#13724)
* Fix Linux segfault when loading Fluidd/Mainsail printer hosts (#7210)

Co-authored-by: VittC <58783944+VittC@users.noreply.github.com>

* narrow down the changes

---------

Co-authored-by: VittC <58783944+VittC@users.noreply.github.com>
2026-05-19 14:26:34 +08:00
Andrew
f78836bef4 Stop tracking .idea files (#13735) 2026-05-19 11:20:04 +08:00
SoftFever
d8369e5f75 Fix faint toolbar icons on Wayland (#13723)
Use glBlendFuncSeparate in GLTexture::render_sub_texture so destination
  alpha stays at 1.0. The Wayland compositor honors framebuffer alpha for     window compositing; the previous straight-alpha blend reduced dst alpha
  at anti-aliased icon edges, making them semi-transparent against the
  desktop. RGB blending is unchanged, so X11/Windows/macOS are unaffected.
2026-05-19 01:38:37 +08:00
SoftFever
88b4a63228 Fix Linux data-view dropdown popup parenting (#13721)
Delay opening the object-list filament dropdown on wxGTK until the editor
  window is mapped, avoiding popup creation without a valid native toplevel.
  Also set the GTK transient parent for dropdown popups created from data-view
  cell editors.
2026-05-19 01:01:53 +08:00
yw4z
dc12126b78 Fix 2 Linux assertation errors on gtk_window_resize() (#13718)
Update DropDown.cpp
2026-05-18 19:01:42 +03:00
yw4z
054a173af7 Fix possible crash on startup because of low max volumetric speed on profiles (#13716)
* Update fdm_filament_common.json

* update

* Update fdm_filament_common.json
2026-05-18 19:00:48 +03:00
Kappa971
248a55abd0 Update Italian translation (#13674) 2026-05-18 10:29:38 -03:00
yw4z
b4aa070c40 MultiChooseDialog & CheckList class & improvements for Profile Dependencies (#9971)
* init

* fix

* fix

* update

* update

* update

* Update Tab.cpp

* Update CheckList.cpp
2026-05-18 19:46:47 +08:00
Thomas Henauer
b9ff15054f Fix wxGTK submenu popup parenting and sizing on Wayland (#13707) 2026-05-18 18:01:19 +08:00
CSLRDoesntGameDev
ddeaa4ba82 Add patches to the Anycubic Kobra X machine json (#13677)
add patched anycubic kobra x machine json

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-05-18 16:57:24 +08:00
Ian Chua
b333b04815 log: add logs surrounding logout to potentially catch any unwanted logouts or errors (#13713)
# Description

Currently, there is some suspicious behavior going on with the logout
flow, adding some logs to potentially catch any unwanted logouts or
unintentional behaviors.


[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-05-18 16:39:59 +08:00
Ioannis Giannakas
e5ca01ba9e Fix per-volume "Only one wall" overrides being ignored on assembly parts (#13714) 2026-05-18 09:24:28 +01:00
83 changed files with 1213 additions and 634 deletions

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

2
.idea/OrcaSlicer.iml generated
View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

View File

@@ -1,7 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

4
.idea/misc.xml generated
View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/OrcaSlicer.iml" filepath="$PROJECT_DIR$/.idea/OrcaSlicer.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@@ -94,8 +94,8 @@ text = Switch workspaces\nYou can switch between <b>Prepare</b> and <b>Preview</
[hint:How to use keyboard shortcuts]
text = How to use keyboard shortcuts\nDid you know that Orca Slicer offers a wide range of keyboard shortcuts and 3D scene operations?
[hint:Reverse on odd]
text = Reverse on odd\nDid you know that <b>Reverse on odd</b> feature can significantly improve the surface quality of your overhangs?
[hint:Reverse on even]
text = Reverse on even\nDid you know that <b>Reverse on even</b> feature can significantly improve the surface quality of your overhangs? However, it can cause wall inconsistencies so use carefully!
[hint:Cut Tool]
text = Cut Tool\nDid you know that you can cut a model at any angle and position with the cutting tool?

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><polygon points=".5 .5 5.5 7.5 5.5 13.5 9.5 15.5 9.5 7.5 14.5 .5 .5 .5" style="fill:none; stroke:#949494; stroke-linecap:round; stroke-linejoin:round;"/><line x1="8.64" y1="4.5" x2="10.19" y2="2.34" style="fill:none; stroke:#949494; stroke-linecap:round; stroke-linejoin:round;"/></svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@@ -61,7 +61,7 @@
"nil"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -1,237 +0,0 @@
{
"type": "machine",
"name": "Anet A8 Plus 0.4 nozzle",
"inherits": "",
"from": "User",
"adaptive_bed_mesh_margin": "0",
"auxiliary_fan": "0",
"bbl_use_printhost": "0",
"bed_custom_model": "",
"bed_custom_texture": "",
"bed_exclude_area": [
"0x0"
],
"bed_mesh_max": "99999,99999",
"bed_mesh_min": "-99999,-99999",
"bed_mesh_probe_distance": "50,50",
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
"best_object_pos": "0.5,0.5",
"change_extrusion_role_gcode": "",
"change_filament_gcode": "M600",
"cooling_tube_length": "5",
"cooling_tube_retraction": "91.5",
"default_bed_type": "",
"default_filament_profile": [
"Anycubic Generic PLA"
],
"default_print_profile": "0.20mm Standard @Anycubic 4MaxPro2",
"deretraction_speed": [
"25"
],
"disable_m73": "0",
"emit_machine_limits_to_gcode": "1",
"enable_filament_ramming": "1",
"enable_long_retraction_when_cut": "0",
"extra_loading_move": "-2",
"extruder_clearance_height_to_lid": "140",
"extruder_clearance_height_to_rod": "36",
"extruder_clearance_radius": "65",
"extruder_colour": [
"#018001"
],
"extruder_offset": [
"0x0"
],
"fan_kickstart": "0",
"fan_speedup_overhangs": "1",
"fan_speedup_time": "0",
"gcode_flavor": "marlin",
"head_wrap_detect_zone": [],
"high_current_on_filament_swap": "0",
"host_type": "octoprint",
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
"long_retractions_when_cut": [
"0"
],
"machine_end_gcode": "M104 S0 ; turn off extruder heating\nM140 S0 ; turn off bed heating\nM107 ; turn off fans\nG91 ; relative positioning\nG0 Z+0.5 ; move Z up a tiny bit\nG90 ; absolute positioning\nG0 X135 Y105 F{machine_max_speed_x[0]*60} ; move extruder to center position\nG0 Z190.5 F{machine_max_speed_z[0]*60} ; lower the plattform to Z min\nM84 ; steppers off\nG90 ; absolute positioning\n",
"machine_load_filament_time": "0",
"machine_max_acceleration_e": [
"5000",
"5000"
],
"machine_max_acceleration_extruding": [
"1250",
"1250"
],
"machine_max_acceleration_retracting": [
"1250",
"1250"
],
"machine_max_acceleration_travel": [
"1500",
"1500"
],
"machine_max_acceleration_x": [
"1000",
"900"
],
"machine_max_acceleration_y": [
"1000",
"900"
],
"machine_max_acceleration_z": [
"1000",
"100"
],
"machine_max_jerk_e": [
"5",
"5"
],
"machine_max_jerk_x": [
"6",
"6"
],
"machine_max_jerk_y": [
"6",
"6"
],
"machine_max_jerk_z": [
"0.2",
"0.2"
],
"machine_max_junction_deviation": [
"0.10",
"0"
],
"machine_max_speed_e": [
"120",
"120"
],
"machine_max_speed_x": [
"200",
"200"
],
"machine_max_speed_y": [
"200",
"200"
],
"machine_max_speed_z": [
"64",
"16"
],
"machine_min_extruding_rate": [
"0",
"0"
],
"machine_min_travel_rate": [
"0",
"0"
],
"machine_pause_gcode": "M601",
"machine_start_gcode": "G21 ; metric values\nG90 ; absolute positioning\nM82 ; set extruder to absolute mode\nM140 S[first_layer_bed_temperature] ; set bed temp\nG28 X0 Y0 ; home X and Y\nG28 Z0 ; home Z\nG1 Z30 F{machine_max_speed_z[0]*60} ; move Z a bit down to not blow on the bed edge while heating\nG1 X10 F3900 ; let some space on x to prevent the filament cooling exhaust from beeing blocked by the servo motor\nM190 S[bed_temperature_initial_layer_single] ; wait for bed temp\nM104 S[nozzle_temperature_initial_layer] ; set extruder temp\nM106 S80 ; turn on fan to prevent air nozzle melt while heating up\nM109 S[nozzle_temperature_initial_layer] ; wait for extruder temp\nM107 ; start with the fan off\nG28 X0 ; goto X home again\nG92 E0 ; zero the extruded length\nG1 Z0.2 F360 ; move plattform upwards\n; extrude material next to the plattform (comment or remove following lines to disable)\nG1 F180 E20 ; extrude some material next to the plattform\nG92 E0 ; zero the extruded length\nG1 E-[retraction_length] F{retraction_speed[0]*60} ; do a filament retract\nG92 E0 ; zero the extruded length again\nG1 X5 F3900 ; move sideways to get rid of that string\nG1 E[retraction_length] F{retraction_speed[0]*60} ; do a filament deretract with retract parameters\nG92 E0 ; zero the extruded length again\n; draw intro line (comment or remove following lines to disable)\nG1 X30 E5 F700 ; draw intro line\nG92 E0 ; zero the extruded length\nG1 E-[retraction_length] F{retraction_speed[0]*60} ; do a filament retract\nG1 X40 Z2.0 ; move away from the introline\nG92 E0 ; zero the extruded length again\nG1 E[retraction_length] F{retraction_speed[0]*60} ; do a filament deretract with retract parameters\n; end of intro line code\nM117 Printing...\nG5",
"machine_tool_change_time": "0",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"max_layer_height": [
"0.3"
],
"max_resonance_avoidance_speed": "120",
"min_layer_height": [
"0.07"
],
"min_resonance_avoidance_speed": "70",
"nozzle_diameter": [
"0.4"
],
"nozzle_height": "2.5",
"nozzle_hrc": "0",
"nozzle_type": "undefine",
"nozzle_volume": "0",
"parking_pos_retraction": "92",
"pellet_modded_printer": "0",
"preferred_orientation": "0",
"printable_area": [
"0x0",
"300x0",
"300x300",
"0x300"
],
"printable_height": "350",
"printer_model": "Anet A8 Plus",
"printer_notes": "",
"printer_settings_id": "Anet A8 Plus 0.4 nozzle",
"printer_structure": "undefine",
"printer_technology": "FFF",
"printer_variant": "0.4",
"printhost_authorization_type": "key",
"printhost_ssl_ignore_revoke": "0",
"printing_by_object_gcode": "",
"purge_in_prime_tower": "1",
"resonance_avoidance": "0",
"retract_before_wipe": [
"0%"
],
"retract_length_toolchange": [
"10"
],
"retract_lift_above": [
"0"
],
"retract_lift_below": [
"0"
],
"retract_lift_enforce": [
"All Surfaces"
],
"retract_restart_extra": [
"0"
],
"retract_restart_extra_toolchange": [
"0"
],
"retract_when_changing_layer": [
"1"
],
"retraction_distances_when_cut": [
"18"
],
"retraction_length": [
"2.5"
],
"retraction_minimum_travel": [
"2"
],
"retraction_speed": [
"35"
],
"scan_first_layer": "0",
"silent_mode": "0",
"single_extruder_multi_material": "1",
"support_air_filtration": "1",
"support_chamber_temp_control": "1",
"support_multi_bed_types": "0",
"template_custom_gcode": "",
"thumbnails": "48x48/PNG,300x300/PNG",
"thumbnails_format": "PNG",
"time_cost": "0",
"time_lapse_gcode": "",
"travel_slope": [
"3"
],
"upward_compatible_machine": [],
"use_firmware_retraction": "0",
"use_relative_e_distances": "1",
"wipe": [
"1"
],
"wipe_distance": [
"1"
],
"z_hop": [
"0.4"
],
"z_hop_types": [
"Normal Lift"
],
"z_offset": "0"
}

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -97,7 +97,7 @@
"nil"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_ramming_volumetric_speed": [
"-1"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -67,7 +67,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -14,7 +14,7 @@
"15"
],
"filament_max_volumetric_speed": [
"0"
"8"
],
"filament_type": [
"PETG"

View File

@@ -6,7 +6,7 @@
"instantiation": "false",
"fan_cooling_layer_time": "100",
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_type": [
"PLA"

View File

@@ -14,7 +14,7 @@
"30"
],
"filament_max_volumetric_speed": [
"0"
"3.6"
],
"filament_type": [
"TPU"

View File

@@ -40,7 +40,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -38,7 +38,7 @@
"15"
],
"filament_max_volumetric_speed": [
"0"
"8"
],
"filament_type": [
"PETG"

View File

@@ -8,7 +8,7 @@
"100"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_type": [
"PLA"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -70,7 +70,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -61,7 +61,7 @@
"nil"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -58,7 +58,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -61,7 +61,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -67,7 +67,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"0"

View File

@@ -61,7 +61,7 @@
"nil"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"1.75"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -61,7 +61,7 @@
"nil"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -64,7 +64,7 @@
"2.85"
],
"filament_max_volumetric_speed": [
"0"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"

View File

@@ -357,29 +357,9 @@ cat << EOF >@SLIC3R_APP_CMD@
DIR=\$(dirname "\$(readlink -f "\$0")")
export APPDIR="\${APPDIR:-\$DIR}"
# FIXME: OrcaSlicer segfault workarounds
# 1) OrcaSlicer will segfault on systems where locale info is not as expected (i.e. Holo-ISO arch-based distro)
# OrcaSlicer may segfault on systems where locale info is not as expected (i.e. Holo-ISO arch-based distro)
export LC_ALL=C
if [ "\$XDG_SESSION_TYPE" = "wayland" ] && [ "\$ZINK_DISABLE_OVERRIDE" != "1" ]; then
if command -v glxinfo >/dev/null 2>&1; then
RENDERER=\$(glxinfo | grep "OpenGL renderer string:" | sed 's/.*: //')
if echo "\$RENDERER" | grep -qi "NVIDIA"; then
if command -v nvidia-smi >/dev/null 2>&1; then
DRIVER_VERSION=\$(nvidia-smi --query-gpu=driver_version --format=csv,noheader | head -n1)
DRIVER_MAJOR=\$(echo "\$DRIVER_VERSION" | cut -d. -f1)
[ "\$DRIVER_MAJOR" -gt 555 ] && ZINK_FORCE_OVERRIDE=1
fi
if [ "\$ZINK_FORCE_OVERRIDE" = "1" ]; then
export __GLX_VENDOR_LIBRARY_NAME=mesa
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
export MESA_LOADER_DRIVER_OVERRIDE=zink
export GALLIUM_DRIVER=zink
export WEBKIT_DISABLE_DMABUF_RENDERER=1
fi
fi
fi
fi
exec "\$DIR/libexec/@SLIC3R_APP_CMD@-env" "\$DIR/bin/@SLIC3R_APP_CMD@" "\$@"
EOF

View File

@@ -719,6 +719,9 @@ std::string CoolingBuffer::apply_layer_cooldown(
// Second generate the adjusted G-code.
std::string new_gcode;
new_gcode.reserve(gcode.size() * 2);
// ORCA: per-printer minimum non-zero fan PWM. Applied at every part-cooling fan emission below so any
// non-zero command is raised to at least this percent (0 disables the clamp).
const unsigned int part_cooling_fan_min_pwm = static_cast<unsigned int>(std::max(0, m_config.part_cooling_fan_min_pwm.value));
bool overhang_fan_control= false;
int overhang_fan_speed = 0;
bool internal_bridge_fan_control= false; // ORCA: Add support for separate internal bridge fan speed control
@@ -727,7 +730,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
int supp_interface_fan_speed = 0;
bool ironing_fan_control= false; // ORCA: Add support for ironing fan speed control
int ironing_fan_speed = 0; // ORCA: Add support for ironing fan speed control
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode,
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, part_cooling_fan_min_pwm,
&overhang_fan_control, &overhang_fan_speed,
&internal_bridge_fan_control, &internal_bridge_fan_speed,
&supp_interface_fan_control, &supp_interface_fan_speed,
@@ -743,11 +746,11 @@ std::string CoolingBuffer::apply_layer_cooldown(
int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed);
if (close_fan_the_first_x_layers <= 0 && full_fan_speed_layer > 0) {
// When ramping up fan speed from close_fan_the_first_x_layers to full_fan_speed_layer, force close_fan_the_first_x_layers above zero,
// so there will be a zero fan speed at least at the 1st layer.
close_fan_the_first_x_layers = 1;
}
// ORCA: previously a silent override forced `close_fan_the_first_x_layers` from 0 up to 1 whenever a ramp
// was configured (`full_fan_speed_layer > 0`), so the first printed layer always had the fan disabled.
// That hid the user's literal "no cooling for the first 0 layers" setting and produced a non-zero starting
// factor on the ramp denominator. The override has been removed: with N=0 and M>0 the ramp now genuinely
// starts on layer 0 at a factor of 1/M and reaches 100% at layer M-1, matching the intent of the option.
if (int(layer_id) >= close_fan_the_first_x_layers) {
float fan_max_speed = EXTRUDER_CONFIG(fan_max_speed);
float slow_down_layer_time = float(EXTRUDER_CONFIG(slow_down_layer_time));
@@ -806,7 +809,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
m_fan_speed = fan_speed_new;
m_current_fan_speed = fan_speed_new;
if (immediately_apply)
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm);
}
//BBS
if (additional_fan_speed_new != m_additional_fan_speed) {
@@ -980,26 +983,26 @@ std::string CoolingBuffer::apply_layer_cooldown(
if (need_set_fan) {
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = overhang_fan_speed;
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = internal_bridge_fan_speed;
}
else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = supp_interface_fan_speed;
}
else if (fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = ironing_fan_speed;
}
else if(fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] && m_current_fan_speed != -1){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed, part_cooling_fan_min_pwm);
fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false;
}
else
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm);
need_set_fan = false;
}
pos = line_end;

View File

@@ -230,7 +230,10 @@ void FanMover::_remove_slow_fan(int16_t min_speed, float past_sec) {
std::string FanMover::_set_fan(int16_t speed) {
//const Tool* tool = m_writer.get_tool(m_currrent_extruder < 20 ? m_currrent_extruder : 0);
return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed);
// ORCA: apply the per-printer non-zero fan PWM floor so reposted fan commands respect the clamp too.
const int floor_pct = m_writer.config.part_cooling_fan_min_pwm.value;
const unsigned int part_cooling_fan_min_pwm = floor_pct > 0 ? static_cast<unsigned int>(floor_pct) : 0u;
return GCodeWriter::set_fan(m_writer.config.gcode_flavor.value, speed, part_cooling_fan_min_pwm);
}

View File

@@ -1092,9 +1092,13 @@ std::string GCodeWriter::unlift()
return gcode;
}
std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed)
std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm)
{
std::ostringstream gcode;
// ORCA: clamp non-zero fan commands up to the configured PWM floor so fans that can't spool at low duty
// cycles still start reliably. Zero (fan off) is preserved exactly so disable-fan commands are never altered.
if (speed > 0 && part_cooling_fan_min_pwm > 0 && speed < part_cooling_fan_min_pwm)
speed = part_cooling_fan_min_pwm;
if (speed == 0) {
switch (gcode_flavor) {
case gcfTeacup:
@@ -1129,7 +1133,9 @@ std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, unsigned int sp
std::string GCodeWriter::set_fan(unsigned int speed) const
{
//BBS
return GCodeWriter::set_fan(this->config.gcode_flavor, speed);
// ORCA: pick up the per-printer PWM floor from the active config.
return GCodeWriter::set_fan(this->config.gcode_flavor, speed,
static_cast<unsigned int>(std::max(0, this->config.part_cooling_fan_min_pwm.value)));
}
//BBS: set additional fan speed for BBS machine only

View File

@@ -98,7 +98,9 @@ public:
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; };
// To be called by the CoolingBuffer from another thread.
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed);
// ORCA: `part_cooling_fan_min_pwm` (0-100, default 0) is a floor applied only when `speed` is non-zero, used to overcome
// PWM start-up thresholds on fans that won't spool below a certain duty cycle. A `speed` of 0 is always honoured.
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed, unsigned int part_cooling_fan_min_pwm = 0);
// To be called by the main thread. It always emits the G-code, it does not remember the previous state.
// Keeping the state is left to the CoolingBuffer, which runs asynchronously on another thread.
std::string set_fan(unsigned int speed) const;

View File

@@ -159,6 +159,12 @@ bool Layer::is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b)
&& config.detect_thin_wall == other_config.detect_thin_wall
&& config.infill_wall_overlap == other_config.infill_wall_overlap
&& config.top_bottom_infill_wall_overlap == other_config.top_bottom_infill_wall_overlap
// Orca: these flags directly change the effective wall count produced by the perimeter
// generator. If two regions disagree on any of them, merging their slices into one shared make_perimeters
// call would silently use the first region's flag for both.
&& config.only_one_wall_first_layer == other_config.only_one_wall_first_layer
&& config.only_one_wall_top == other_config.only_one_wall_top
&& config.min_width_top_surface == other_config.min_width_top_surface
&& config.seam_slope_type == other_config.seam_slope_type
&& config.seam_slope_conditional == other_config.seam_slope_conditional
&& config.scarf_angle_threshold == other_config.scarf_angle_threshold

View File

@@ -1326,7 +1326,7 @@ static std::vector<std::string> s_Preset_machine_limits_options {
static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
"fan_kickstart", "part_cooling_fan_min_pwm", "fan_speedup_time", "fan_speedup_overhangs",
"single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
"printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type",
"printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",

View File

@@ -133,6 +133,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"fan_cooling_layer_time",
"full_fan_speed_layer",
"fan_kickstart",
"part_cooling_fan_min_pwm",
"fan_speedup_overhangs",
"fan_speedup_time",
"filament_colour",

View File

@@ -3724,6 +3724,28 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
// ORCA: minimum non-zero part cooling fan speed.
def = this->add("part_cooling_fan_min_pwm", coInt);
def->label = L("Minimum non-zero part cooling fan speed");
def->tooltip = L("Some part-cooling fans cannot start spinning when commanded below a certain PWM duty cycle. "
"When set above 0, any non-zero part-cooling fan command will be raised to at least this percentage "
"so the fan reliably starts. A fan command of 0 (fan off) is always honoured exactly. "
"This clamp is applied after every other fan calculation (first-layer ramp, layer-time interpolation, "
"overhang/bridge/support-interface/ironing overrides), so scaling still operates within the range "
"[this value, 100%]."
"\nIf your firmware already disables the fan below a threshold (for example Klipper's "
"[fan] off_below: 0.10 shuts the fan off whenever the commanded duty cycle is below 10%), "
"this option and the firmware threshold should ideally be set to the same value. Matching them "
"(e.g. off_below: 0.10 in Klipper and 10% here) guarantees the slicer never emits a non-zero "
"value that the firmware would silently drop, and the fan never receives a value below the one "
"you know it can actually spool at."
"\nSet to 0 to deactivate.");
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("time_cost", coFloat);
def->label = L("Time cost");

View File

@@ -1309,6 +1309,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, fan_kickstart))
((ConfigOptionBool, fan_speedup_overhangs))
((ConfigOptionFloat, fan_speedup_time))
// ORCA: minimum PWM (as a percent 0-100) emitted when the part-cooling fan is asked for a non-zero speed.
// Used to overcome the PWM start-up threshold on fans that cannot spool below a certain duty cycle.
// A value of 0 (the default) leaves behaviour unchanged. A fan command of 0 (off) is always honoured.
((ConfigOptionInt, part_cooling_fan_min_pwm))
((ConfigOptionFloats, filament_diameter))
((ConfigOptionBoolsNullable, filament_adaptive_volumetric_speed))
((ConfigOptionStrings, volumetric_speed_coefficients))

View File

@@ -329,6 +329,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Mouse3DController.hpp
GUI/MsgDialog.cpp
GUI/MsgDialog.hpp
GUI/MultiChoiceDialog.hpp
GUI/MultiChoiceDialog.cpp
GUI/MultiMachine.cpp
GUI/MultiMachine.hpp
GUI/MultiMachineManagerPage.cpp
@@ -496,6 +498,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Widgets/Button.hpp
GUI/Widgets/CheckBox.cpp
GUI/Widgets/CheckBox.hpp
GUI/Widgets/CheckList.cpp
GUI/Widgets/CheckList.hpp
GUI/Widgets/ComboBox.cpp
GUI/Widgets/ComboBox.hpp
GUI/Widgets/DialogButtons.cpp
@@ -683,6 +687,8 @@ if (APPLE)
GUI/Mouse3DHandlerMac.mm
GUI/InstanceCheckMac.mm
GUI/InstanceCheckMac.h
GUI/DeepLinkHandlerMac.mm
GUI/DeepLinkHandlerMac.h
GUI/GUI_UtilsMac.mm
GUI/wxMediaCtrl2.mm
GUI/wxMediaCtrl2.h

View File

@@ -0,0 +1,16 @@
#ifndef slic3r_GUI_DeepLinkHandlerMac_h_
#define slic3r_GUI_DeepLinkHandlerMac_h_
namespace Slic3r {
namespace GUI {
// Re-registers a Cocoa Apple Event handler for kInternetEventClass/kAEGetURL.
// Works around a regression observed after upgrading to wxWidgets 3.3.2 on
// macOS Tahoe (#13119) where wxWidgets' built-in handler is registered but
// never fires for orcaslicer:// deep links.
void register_mac_deep_link_handler();
} // namespace GUI
} // namespace Slic3r
#endif

View File

@@ -0,0 +1,41 @@
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <boost/log/trivial.hpp>
#include "DeepLinkHandlerMac.h"
#include "GUI_App.hpp"
@interface OrcaDeepLinkHandler : NSObject
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply;
@end
@implementation OrcaDeepLinkHandler
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
{
NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
if (url == nil || url.length == 0)
return;
BOOST_LOG_TRIVIAL(info) << "Deep link received: " << [url UTF8String];
Slic3r::GUI::wxGetApp().MacOpenURL(wxString::FromUTF8([url UTF8String]));
}
@end
namespace Slic3r {
namespace GUI {
void register_mac_deep_link_handler()
{
static OrcaDeepLinkHandler *handler = nil;
if (handler == nil)
handler = [[OrcaDeepLinkHandler alloc] init];
[[NSAppleEventManager sharedAppleEventManager]
setEventHandler:handler
andSelector:@selector(handleGetURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
}
} // namespace GUI
} // namespace Slic3r

View File

@@ -324,9 +324,18 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
else
c_editor->SetSelection(atoi(data.GetText().c_str()) - 1);
// Open the dropdown immediately when the editor is focused.
c_editor->Bind(wxEVT_SET_FOCUS, [c_editor](wxFocusEvent& evt) {
#ifdef __WXGTK__
// On wxGTK the data-view editor may receive focus before its native
// window is mapped. Opening the popup one event later avoids creating
// the GTK popup without a valid toplevel parent.
c_editor->CallAfter([c_editor]() {
if (c_editor->IsShownOnScreen())
c_editor->ForceDropdownOpen();
});
#else
c_editor->ForceDropdownOpen();
#endif
evt.Skip();
});
@@ -392,4 +401,3 @@ wxSize TextRenderer::GetSize() const
return GetTextExtent(m_value);
}

View File

@@ -663,7 +663,9 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs)
{
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Orca: fix washed-out toolbar icons on Wayland: keep destination alpha at 1.0 so the compositor
// does not treat anti-aliased icon edges as window transparency.
glsafe(::glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnable(GL_TEXTURE_2D));
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));

View File

@@ -100,6 +100,9 @@
#include "Mouse3DController.hpp"
#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
#ifdef __APPLE__
#include "DeepLinkHandlerMac.h"
#endif
#include "NotificationManager.hpp"
#include "UnsavedChangesDialog.hpp"
#include "SavePresetDialog.hpp"
@@ -2547,6 +2550,12 @@ std::string get_system_info()
bool GUI_App::on_init_inner()
{
wxLog::SetActiveTarget(new wxBoostLog());
#ifdef __APPLE__
// Override wxWidgets' kAEGetURL handler so orcaslicer:// deep links keep
// working after the wxWidgets 3.3.2 upgrade on macOS (#13119).
register_mac_deep_link_handler();
#endif
#if BBL_RELEASE_TO_PUBLIC
wxLog::SetLogLevel(wxLOG_Message);
#endif

View File

@@ -561,14 +561,23 @@ wxString GridCellSupportEditor::ms_stringValues[2] = { wxT(""), wxT("") };
void GridCellSupportEditor::DoActivate(int row, int col, wxGrid* grid)
{
ObjectGrid* local_table = dynamic_cast<ObjectGrid*>(grid);
wxGridBlocks cell_array = grid->GetSelectedBlocks();
auto left_col = cell_array.begin()->GetLeftCol();
auto right_col = cell_array.begin()->GetRightCol();
auto top_row = cell_array.begin()->GetTopRow();
auto bottom_row = cell_array.begin()->GetBottomRow();
auto iter = cell_array.begin();
int left_col, right_col, top_row, bottom_row;
if (iter == cell_array.end()) {
// wxWidgets 3.3.x returns an empty range when nothing is selected;
// fall back to the cell that triggered activation so the single-cell
// branch below handles it.
left_col = right_col = col;
top_row = bottom_row = row;
} else {
left_col = iter->GetLeftCol();
right_col = iter->GetRightCol();
top_row = iter->GetTopRow();
bottom_row = iter->GetBottomRow();
}
if ((left_col == right_col) &&
(top_row == bottom_row)) {
wxGridCellBoolEditor::DoActivate(row, col, grid);

View File

@@ -0,0 +1,58 @@
#include "MultiChoiceDialog.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
namespace Slic3r { namespace GUI {
MultiChoiceDialog::MultiChoiceDialog(
wxWindow* parent,
const wxString& message,
const wxString& caption,
const wxArrayString& choices
)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, caption, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
SetBackgroundColour(*wxWHITE);
wxBoxSizer* w_sizer = new wxBoxSizer(wxVERTICAL);
if(!message.IsEmpty()){
wxStaticText *msg = new wxStaticText(this, wxID_ANY, message);
msg->SetFont(Label::Body_13);
msg->Wrap(-1);
w_sizer->Add(msg, 0, wxRIGHT | wxLEFT | wxTOP, FromDIP(10));
}
m_check_list = new CheckList(this, choices);
w_sizer->Add(m_check_list, 1, wxRIGHT | wxLEFT | wxTOP | wxEXPAND, FromDIP(10));
auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"});
dlg_btns->GetOK()->Bind( wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_OK);});
dlg_btns->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_CANCEL);});
w_sizer->Add(dlg_btns, 0, wxEXPAND);
SetSizer(w_sizer);
Layout();
w_sizer->Fit(this);
wxGetApp().UpdateDlgDarkUI(this);
}
wxArrayInt MultiChoiceDialog::GetSelections() const
{
return m_check_list->GetSelections();
}
void MultiChoiceDialog::SetSelections(wxArrayInt sel_array)
{
m_check_list->SetSelections(sel_array);
}
MultiChoiceDialog::~MultiChoiceDialog() {}
void MultiChoiceDialog::on_dpi_changed(const wxRect &suggested_rect) {}
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,37 @@
#ifndef slic3r_GUI_MultiChoiceDialog_hpp_
#define slic3r_GUI_MultiChoiceDialog_hpp_
#include "Widgets/CheckList.hpp"
#include "Widgets/DialogButtons.hpp"
#include <wx/wx.h>
#include <vector>
#include <map>
namespace Slic3r { namespace GUI {
class MultiChoiceDialog : public DPIDialog
{
public:
MultiChoiceDialog(
wxWindow* parent = nullptr,
const wxString& message = wxEmptyString,
const wxString& caption = wxEmptyString,
const wxArrayString& choices = wxArrayString()
);
~MultiChoiceDialog();
wxArrayInt GetSelections() const;
void SetSelections(wxArrayInt sel_array);
protected:
CheckList* m_check_list;
wxArrayInt m_selected_indices;
void on_dpi_changed(const wxRect &suggested_rect) override;
};
}} // namespace Slic3r::GUI
#endif

View File

@@ -23,6 +23,81 @@
namespace Slic3r {
namespace GUI {
#ifdef __linux__
// Workaround for #7210: WebKitGTK crashes on vue-resize's hidden <object> probe used by
// older Fluidd/Mainsail pages. Swap that <object> for a <div> shim at appendChild time
// and bridge resize events through a fake contentDocument.defaultView so vue-resize keeps
// working. Workaround proposed by @VittC.
static void inject_vue_resize_workaround(wxWebView *webView)
{
webView->AddUserScript(
"(function() {"
" 'use strict';"
" function isVueResizeObject(el) {"
" return el && el.tagName === 'OBJECT'"
" && el.type === 'text/html'"
" && el.getAttribute('aria-hidden') === 'true'"
" && el.getAttribute('tabindex') === '-1';"
" }"
" function isResizeObserverParent(p) {"
" return p && p.classList && p.classList.contains('resize-observer');"
" }"
" function makeShim(orig, parentForRO) {"
" var shim = document.createElement('div');"
" shim.setAttribute('aria-hidden', 'true');"
" shim.setAttribute('tabindex', '-1');"
" shim.style.display = 'none';"
" var fakeWin = document.createElement('div');"
" var ro = null;"
" var origRemoveEL = fakeWin.removeEventListener.bind(fakeWin);"
" fakeWin.removeEventListener = function(type, fn, opts) {"
" origRemoveEL(type, fn, opts);"
" if (type === 'resize' && ro) { ro.disconnect(); ro = null; }"
" };"
" Object.defineProperty(shim, 'contentDocument', {"
" configurable: true,"
" get: function() { return { defaultView: fakeWin }; }"
" });"
" Object.defineProperty(shim, 'contentWindow', {"
" configurable: true,"
" get: function() { return fakeWin; }"
" });"
" if (typeof orig.onload === 'function') { shim.onload = orig.onload; }"
" queueMicrotask(function() {"
" if (parentForRO && typeof ResizeObserver !== 'undefined') {"
" ro = new ResizeObserver(function() {"
" fakeWin.dispatchEvent(new Event('resize'));"
" });"
" ro.observe(parentForRO);"
" }"
" if (typeof shim.onload === 'function') {"
" try { shim.onload(new Event('load')); } catch (e) {}"
" }"
" shim.dispatchEvent(new Event('load'));"
" });"
" return shim;"
" }"
" var origAppend = Node.prototype.appendChild;"
" Node.prototype.appendChild = function(child) {"
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
" return origAppend.call(this, makeShim(child, this));"
" }"
" return origAppend.call(this, child);"
" };"
" var origInsertBefore = Node.prototype.insertBefore;"
" Node.prototype.insertBefore = function(child, ref) {"
" if (isResizeObserverParent(this) && isVueResizeObject(child)) {"
" return origInsertBefore.call(this, makeShim(child, this), ref);"
" }"
" return origInsertBefore.call(this, child, ref);"
" };"
" console.log('[vr-fix] vue-resize WebKitGTK patch active');"
"})();",
wxWEBVIEW_INJECT_AT_DOCUMENT_START
);
}
#endif
PrinterWebView::PrinterWebView(wxWindow *parent)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
, m_browser(nullptr)
@@ -43,6 +118,8 @@ PrinterWebView::PrinterWebView(wxWindow *parent)
}
#ifdef __linux__
inject_vue_resize_workaround(m_browser);
auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db");
auto wv = static_cast<WebKitWebView*>(m_browser->GetNativeBackend());
auto wv_ctx = webkit_web_view_get_context(wv);
@@ -165,6 +242,10 @@ void PrinterWebView::SendAPIKey()
)",
m_apikey);
m_browser->RemoveAllUserScripts();
#ifdef __linux__
// Re-inject the vue-resize/WebKitGTK workaround that RemoveAllUserScripts just cleared.
inject_vue_resize_workaround(m_browser);
#endif
m_browser->AddUserScript(script);
m_browser->Reload();

View File

@@ -40,7 +40,7 @@
#include "UnsavedChangesDialog.hpp"
#include "SavePresetDialog.hpp"
#include "EditGCodeDialog.hpp"
#include "MultiChoiceDialog.hpp"
#include "MsgDialog.hpp"
#include "Notebook.hpp"
@@ -4500,6 +4500,8 @@ void TabPrinter::build_fff()
line.append_option(optgroup->get_option("fan_speedup_overhangs"));
optgroup->append_line(line);
optgroup->append_single_option_line("fan_kickstart", "printer_basic_information_cooling_fan#fan-kick-start-time");
// ORCA: PWM floor for fans that won't spool at low duty cycles.
optgroup->append_single_option_line("part_cooling_fan_min_pwm", "printer_basic_information_cooling_fan#minimum-non-zero-part-cooling-fan-speed");
optgroup = page->new_optgroup(L("Extruder Clearance"), "param_extruder_clearance");
optgroup->append_single_option_line("extruder_clearance_radius", "printer_basic_information_extruder_clearance#radius");
@@ -7002,7 +7004,15 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
presets.Add(from_u8(preset.name));
}
wxMultiChoiceDialog dlg(parent, deps.dialog_title, deps.dialog_label, presets);
if(deps.type == Preset::TYPE_PRINTER){
deps.dialog_title = "Compatible printers";
deps.dialog_label = "Select printers";
}else{
deps.dialog_title = "Compatible process profiles";
deps.dialog_label = "Select profiles";
}
MultiChoiceDialog dlg(parent, deps.dialog_label, deps.dialog_title, presets);
wxGetApp().UpdateDlgDarkUI(&dlg);
// Collect and set indices of depending_presets marked as compatible.
wxArrayInt selections;
@@ -7015,13 +7025,16 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
break;
}
dlg.SetSelections(selections);
dlg.SetSize(FromDIP(wxSize(360, 480)));
std::vector<std::string> value;
// Show the dialog.
if (dlg.ShowModal() == wxID_OK) {
selections.Clear();
selections = dlg.GetSelections();
for (auto idx : selections)
value.push_back(presets[idx].ToUTF8().data());
// leave list empty if all items checked. this will check "All" checkbox automatically. also fixes unnecessary config change
if(selections.GetCount() != presets.GetCount())
for (auto idx : selections)
value.push_back(presets[idx].ToUTF8().data());
if (value.empty()) {
deps.checkbox->SetValue(1);
deps.btn->Disable();

View File

@@ -0,0 +1,225 @@
#include "CheckList.hpp"
#include "slic3r/GUI/GUI_App.hpp"
CheckList::CheckList(
wxWindow* parent,
const wxArrayString& choices,
long scroll_style
)
: wxWindow(parent, wxID_ANY)
, m_search(this, "search", 16)
, m_menu(this, "filter", 16)
, m_first_load(true)
{
Freeze();
w_sizer = new wxBoxSizer(wxVERTICAL);
f_sizer = new wxBoxSizer(wxHORIZONTAL);
f_bar = new wxPanel(this, wxID_ANY);
f_bar->SetBackgroundColour(parent->GetBackgroundColour());
m_filter_box = new TextInput(f_bar, "", "", "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_filter_box->SetIcon(m_search.bmp());
m_filter_box->SetMinSize(FromDIP(wxSize(200,24)));
m_filter_box->SetSize(FromDIP(wxSize(-1,24)));
m_filter_box->SetFocus();
m_filter_ctrl = m_filter_box->GetTextCtrl();
m_filter_ctrl->SetFont(Label::Body_13);
m_filter_ctrl->SetSize(wxSize(-1, FromDIP(16))); // Centers text vertically
m_filter_ctrl->SetHint(_L("Type to filter..."));
m_filter_ctrl->Bind(wxEVT_TEXT, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
m_filter_ctrl->Bind(wxEVT_TEXT_ENTER, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
m_filter_ctrl->Bind(wxEVT_SET_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
m_filter_ctrl->Bind(wxEVT_KILL_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
f_sizer->Add(m_filter_box, 1, wxEXPAND);
Bind(wxEVT_SET_FOCUS, [this](auto &e) {m_filter_box->SetFocus();});
fb_sizer = new wxBoxSizer(wxHORIZONTAL);
auto create_btn = [this] (wxString title, bool select){
auto btn = new wxStaticText(f_bar, wxID_ANY, title);
btn->SetForegroundColour("#009687");
btn->SetCursor(wxCURSOR_HAND);
btn->SetFont(Label::Body_13);
btn->Bind(wxEVT_LEFT_DOWN, [this, select](wxMouseEvent &e) {SelectAll(select);});
fb_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
};
f_sizer->Add(fb_sizer,0 ,wxALIGN_CENTER_VERTICAL);
create_btn(_L("All") , true);
create_btn(_L("None"), false);
m_menu_button = new wxStaticBitmap(f_bar, wxID_ANY, m_menu.bmp());
m_menu_button->SetCursor(wxCURSOR_HAND);
m_menu_button->Bind(wxEVT_LEFT_DOWN, &CheckList::ShowMenu, this);
f_sizer->Add(m_menu_button,0 ,wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
f_bar->SetSizerAndFit(f_sizer);
w_sizer->Add(f_bar, 0, wxEXPAND);
auto spacer = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(3)));
spacer->SetBackgroundColour(parent->GetBackgroundColour());
w_sizer->Add(spacer, 0, wxEXPAND);
s_sizer = new wxBoxSizer(wxVERTICAL);
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, scroll_style);
m_scroll_area->SetScrollRate(0, 10);
m_scroll_area->SetSizer(s_sizer);
m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour());
m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this);
m_scroll_area->DisableFocusFromKeyboard();
m_info = new wxStaticText(m_scroll_area, wxID_ANY, "");
m_info->SetFont(Label::Body_13);
s_sizer->Add(m_info, 1, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(10));
m_info->Hide();
m_info_nonsel = _L("No selected items...");
m_info_allsel = _L("All items selected...");
m_info_empty = _L("No matching items...");
SetBackgroundColour(StateColor::darkModeColorFor("#DBDBDB")); // draws border on wxScrolledWindow
w_sizer->Add(m_scroll_area, 1, wxEXPAND | wxALL, FromDIP(1)); // 1 for border
s_sizer->Layout();
m_list_size = choices.size();
m_checks.reserve(m_list_size);
auto margin = FromDIP(2);
wxCheckBox* cb;
for (size_t i = 0; i < m_list_size; ++i){
cb = new wxCheckBox(m_scroll_area, wxID_ANY, choices[i]);
m_checks.emplace_back(cb);
s_sizer->Add(cb, 0, wxALL, margin);
}
m_scroll_area->FitInside();
s_sizer->Layout();
SetSizer(w_sizer);
Layout();
Thaw();
}
void CheckList::SetSelections(wxArrayInt sel_array){
if(!m_first_load){
for (size_t i = 0; i < m_list_size; ++i)
Check(i, false);
m_first_load = false;
}
for (int i : sel_array)
Check(i, true);
}
wxArrayInt CheckList::GetSelections()
{
wxArrayInt checks;
for (size_t i = 0; i < m_list_size; ++i)
if (m_checks[i]->GetValue())
checks.push_back(i);
return checks;
}
void CheckList::Check(int i, bool checked)
{
if (i > -1 && i < m_list_size)
m_checks[i]->SetValue(checked);
}
void CheckList::SelectAll(bool value)
{
for (size_t i = 0; i < m_list_size; ++i)
Check(i, value);
}
void CheckList::SelectVisible(bool value)
{
auto filter = m_filter_ctrl->GetValue().Lower();
if((!value && filter == "::unsel") || (value && filter == "::sel"))
m_filter_ctrl->SetValue("");
for (size_t i = 0; i < m_list_size; ++i)
if(m_checks[i]->IsShown())
Check(i, value);
}
bool CheckList::IsChecked(int i)
{
if (i > -1 && i < m_list_size)
return false;
return m_checks[i]->GetValue();
}
void CheckList::Filter(const wxString& filterText)
{
Freeze();
auto filter = filterText.Lower();
auto c_text = m_filter_ctrl->GetValue().Lower();
if(filter == "::sel" || filter == "::nonsel"){
if(c_text != filter){ // not text input
m_filter_ctrl->SetValue(filter);
m_filter_ctrl->SetSelection(0,-1);
}
fb_sizer->Show(false);
if (filter == "::sel")
for (auto& cb : m_checks) cb->Show(cb->GetValue());
else
for (auto& cb : m_checks) cb->Show(!cb->GetValue());
}
else{
bool clear = filterText.IsEmpty();
fb_sizer->Show(clear);
for (auto& cb : m_checks)
cb->Show(clear || cb->GetLabel().Lower().Contains(filter));
}
m_info->Show();
for (size_t i = 0; i < m_list_size; ++i) {
if (m_checks[i]->IsShown()){
m_info->Hide();
break;
}
}
if (m_info->IsShown())
m_info->SetLabel(filter == "::sel" ? m_info_nonsel : filter == "::nonsel" ? m_info_allsel : m_info_empty);
m_scroll_area->FitInside();
f_sizer->Layout();
Thaw();
}
void CheckList::ShowMenu(wxMouseEvent &evt)
{
bool filtering = !m_filter_ctrl->GetValue().IsEmpty();
bool list_empty = m_info->IsShown();
wxMenu m;
m.Append(wxID_FILE1, _L("Select All" ))->Enable(!filtering);
m.Append(wxID_FILE2, _L("Deselect All"))->Enable(!filtering);
m.AppendSeparator();
m.Append(wxID_FILE3, _L("Select visible" ))->Enable(!list_empty && filtering);
m.Append(wxID_FILE4, _L("Deselect visible"))->Enable(!list_empty && filtering);
m.AppendSeparator();
m.Append(wxID_FILE5, _L("Filter selected" ));
m.Append(wxID_FILE6, _L("Filter nonSelected"));
m.Bind(wxEVT_MENU, [this](wxCommandEvent& e) {
switch (e.GetId()){
case wxID_FILE1: SelectAll(true) ; break;
case wxID_FILE2: SelectAll(false) ; break;
case wxID_FILE3: SelectVisible(true) ; break;
case wxID_FILE4: SelectVisible(false); break;
case wxID_FILE5: Filter("::sel") ; break;
case wxID_FILE6: Filter("::nonsel") ; break;
default: break;
}
},wxID_FILE1, wxID_FILE6);
wxWindow* src = dynamic_cast<wxWindow*>(evt.GetEventObject());
if (!src) return;
wxPoint screen_pos = src->ClientToScreen(evt.GetPosition());
wxPoint local_pos = ScreenToClient(screen_pos);
PopupMenu(&m, local_pos);
}

View File

@@ -0,0 +1,59 @@
#ifndef slic3r_GUI_CHECKLIST_hpp_
#define slic3r_GUI_CHECKLIST_hpp_
#include "../wxExtensions.hpp"
#include "Label.hpp"
#include "TextInput.hpp"
#include <wx/wx.h>
#include <vector>
#include <wx/scrolwin.h>
#include <wx/menu.h>
class CheckList : public wxWindow
{
public:
CheckList(
wxWindow* parent,
const wxArrayString& choices = wxArrayString(),
long scroll_style = wxVSCROLL
);
wxArrayInt GetSelections();
void SetSelections(wxArrayInt sel_array);
void Check(int i, bool checked);
bool IsChecked(int i);
void Filter(const wxString& filterText);
void SelectAll(bool value);
void SelectVisible(bool value);
private:
void ShowMenu(wxMouseEvent &e);
std::vector<wxCheckBox*> m_checks;
int m_list_size;
bool m_first_load;
wxBoxSizer* w_sizer;
wxPanel* f_bar;
wxBoxSizer* f_sizer;
TextInput* m_filter_box;
wxTextCtrl* m_filter_ctrl;
wxBoxSizer* fb_sizer;
wxStaticBitmap* m_menu_button;
wxScrolledWindow* m_scroll_area;
wxBoxSizer* s_sizer;
wxStaticText* m_info;
wxString m_info_nonsel;
wxString m_info_allsel;
wxString m_info_empty;
ScalableBitmap m_search;
ScalableBitmap m_menu;
};
#endif // !slic3r_GUI_CheckList_hpp_

View File

@@ -167,6 +167,30 @@ bool DropDown::HasDismissLongTime()
(now - dismissTime).total_milliseconds() >= 20;
}
void DropDown::Popup(wxWindow *focus)
{
#ifdef __WXGTK__
if (!mainDropDown && m_widget) {
// Data-view cell editors can receive focus before wxGTK infers a
// native popup parent, so provide the current toplevel explicitly.
GtkWindow *transient_parent = nullptr;
for (wxWindow *win = GetParent(); win; win = win->GetParent()) {
GtkWidget *widget = static_cast<GtkWidget *>(win->GetHandle());
if (!widget)
continue;
GtkWidget *top = gtk_widget_get_toplevel(widget);
if (GTK_IS_WINDOW(top)) {
transient_parent = GTK_WINDOW(top);
break;
}
}
if (transient_parent)
gtk_window_set_transient_for(GTK_WINDOW(m_widget), transient_parent);
}
#endif
PopupWindow::Popup(focus);
}
void DropDown::paintEvent(wxPaintEvent& evt)
{
// depending on your system you may need to look at double-buffered dcs
@@ -554,7 +578,8 @@ void DropDown::messureSize()
wxWindow::SetSize(szContent);
#ifdef __WXGTK__
// Gtk has a wrapper window for popup widget
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
if (szContent.x > 0 && szContent.y > 0)
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
#endif
if (!groups.empty() && subDropDown == nullptr) {
subDropDown = new DropDown(items);
@@ -564,11 +589,6 @@ void DropDown::messureSize()
subDropDown->use_content_width = true;
subDropDown->Create(GetParent());
#ifdef __WXGTK__
// Orca: Keep the wx parent as the combobox so wxPopupTransientWindow installs
// its capture handlers on the main dropdown, but make the native GTK
// popup transient for the currently open popup to satisfy Wayland's
// xdg-shell rule that a popup's parent must be the topmost mapped popup.
gtk_window_set_transient_for(GTK_WINDOW(subDropDown->GetHandle()), GTK_WINDOW(GetHandle()));
// Orca: On Wayland, while the sub holds an xdg_popup grab, motion events for
// the cursor over main may not be delivered (Mutter drops motion
// outside the grabbing surface). Poll on idle and synthesize a
@@ -636,8 +656,11 @@ void DropDown::autoPosition()
// may exceed
auto drect = wxDisplay(GetParent()).GetGeometry();
if (GetPosition().y + size.y + 10 > drect.GetBottom()) {
int available_height = drect.GetBottom() - GetPosition().y - 10;
if (available_height < rowSize.y * 2)
return;
if (use_content_width && count <= 15) size.x += 6;
size.y = drect.GetBottom() - GetPosition().y - 10;
size.y = available_height;
wxWindow::SetSize(size);
if (selection >= 0) {
if (offset.y + rowSize.y * (selection + 1) > size.y)
@@ -727,6 +750,13 @@ void DropDown::mouseMove(wxMouseEvent &event)
drop.group = items[-index - 2].group_key;
drop.need_sync = true;
drop.messureSize();
#ifdef __WXGTK__
// wxGTK wraps popup contents in a native GtkWindow. Make the submenu
// transient for the currently mapped parent popup window before
// positioning/showing it, so wlroots/Hyprland sees the topmost parent.
if (m_widget && drop.m_widget)
gtk_window_set_transient_for(GTK_WINDOW(drop.m_widget), GTK_WINDOW(m_widget));
#endif
drop.autoPosition();
drop.paintNow();
if (!drop.IsShown())

View File

@@ -105,6 +105,8 @@ public:
bool HasDismissLongTime();
void Popup(wxWindow *focus = nullptr) override;
protected:
void Dismiss() override;