Merge branch '2.3.0' into dev_2.3.0_alves

This commit is contained in:
alves
2026-03-11 10:17:23 +08:00
29 changed files with 337 additions and 159 deletions
+1 -1
View File
@@ -151,7 +151,7 @@ jobs:
if-no-files-found: error
- name: Deploy Flatpak to nightly release
if: ${{github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0'}}
if: ${{github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3'}}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
+11 -11
View File
@@ -122,7 +122,7 @@ jobs:
# Thanks to RaySajuuk, it's working now
- name: Sign app and notary
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14'
if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14'
working-directory: ${{ github.workspace }}
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
@@ -175,7 +175,7 @@ jobs:
fi
- name: Create DMG without notary
if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14' && github.ref != 'refs/heads/2.2.0'
if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14' && github.ref != 'refs/heads/2.2.3'
working-directory: ${{ github.workspace }}
run: |
mkdir -p ${{ github.workspace }}/build/universal/Snapmaker_Orca_dmg
@@ -217,7 +217,7 @@ jobs:
if-no-files-found: warn
- name: Deploy Mac release
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
@@ -228,7 +228,7 @@ jobs:
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Check if profile validator DMG exists
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14'
id: check_dmg
working-directory: ${{ github.workspace }}
run: |
@@ -242,7 +242,7 @@ jobs:
shell: bash
- name: Deploy Mac Snapmaker_Orca_profile_validator DMG release
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'macos-14' && steps.check_dmg.outputs.exists == 'true'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'macos-14' && steps.check_dmg.outputs.exists == 'true'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
@@ -318,7 +318,7 @@ jobs:
path: ${{ github.workspace }}/build/src/Release/Snapmaker_Orca_profile_validator.exe
- name: Deploy Windows release portable
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
@@ -329,7 +329,7 @@ jobs:
max_releases: 1
- name: Deploy Windows release installer
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
@@ -340,7 +340,7 @@ jobs:
max_releases: 1
- name: Deploy Windows Snapmaker_Orca_profile_validator release
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'windows-latest'
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'windows-latest'
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/Snapmaker/OrcaSlicer/releases/169912305/assets{?name,label}
@@ -414,7 +414,7 @@ jobs:
path: './build/src/Release/Snapmaker_Orca_profile_validator'
- name: Deploy Ubuntu release
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
env:
ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }}
uses: WebFreak001/deploy-nightly@v3.2.0
@@ -426,7 +426,7 @@ jobs:
asset_content_type: application/octet-stream
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Deploy Ubuntu release
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && inputs.os == 'ubuntu-24.04' }}
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && inputs.os == 'ubuntu-24.04' }}
uses: rickstaa/action-create-tag@v1
with:
tag: "nightly-builds"
@@ -435,7 +435,7 @@ jobs:
message: "nightly-builds"
- name: Deploy Ubuntu Snapmaker_Orca_profile_validator release
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.0') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
if: ${{ ! env.ACT && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.2.3') && (inputs.os == 'ubuntu-20.04' || inputs.os == 'ubuntu-24.04') }}
env:
ubuntu-ver-str: ${{ (inputs.os == 'ubuntu-24.04' && '_Ubuntu2404') || '' }}
uses: WebFreak001/deploy-nightly@v3.2.0
@@ -15259,7 +15259,7 @@ msgid "Point and point assembly"
msgstr "点和点组装"
msgid "Please do not include the special characters #, *, ;, \\, /, :, \", <, >, or | in filenames."
msgstr "在文件名中勿使用特殊字符"#"*""";""\\""/" ":"\""<"">"或"|"。"
msgstr "在文件名中勿使用特殊字符#, *, ;, \\, /, :, \", <, >或|。"
msgid "The filename '%s' contains special characters (#, *, ;, \\, /, :, \", <, >, or |) which may cause issues.Do you wish to continue?"
msgstr "文件名'%s'包含特殊字符(# * ; \\ / : \" < > |)可能会导致问题。是否继续?"
@@ -194,6 +194,7 @@
"0"
],
"ramming_pressure_advance_value": "0.02",
"default_print_profile": "0.20 Standard @Snapmaker U1 (0.4 nozzle)",
"tool_change_temprature_wait": "0",
"printer_notes": "",
"machine_pause_gcode": "M600",
@@ -34,5 +34,6 @@
"support_threshold_angle": "25",
"support_type": "tree(auto)",
"wipe_tower_extra_rib_length": "8",
"wipe_tower_wall_type": "rib"
"wipe_tower_wall_type": "rib",
"gap_fill_target": "topbottom"
}
@@ -1,27 +0,0 @@
{
"type": "process",
"name": "0.14 Standard @Snapmaker U1 (0.2 nozzle)",
"inherits": "fdm_process_U1_0.14_nozzle_0.2",
"from": "system",
"setting_id": "GP027",
"instantiation": "true",
"description": "Compared with the default profile of a 0.2 mm nozzle, it has a bigger layer height, and results in slightly visible layer lines, but shorter printing time.",
"elefant_foot_compensation": "0.15",
"smooth_coefficient": "150",
"overhang_totally_speed": "50",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"enable_arc_fitting": "0",
"ooze_prevention": "1",
"precise_outer_wall": "0",
"prime_tower_brim_width": "5",
"prime_tower_width": "30",
"prime_volume": "32",
"slowdown_for_curled_perimeters": "0",
"standby_temperature_delta": "-150",
"support_type": "tree(auto)",
"wipe_tower_extra_rib_length": "8",
"wipe_tower_extra_spacing": "120%",
"wipe_tower_wall_type": "rib"
}
@@ -34,5 +34,6 @@
"support_threshold_angle": "30",
"support_type": "tree(auto)",
"wipe_tower_extra_rib_length": "8",
"wipe_tower_wall_type": "rib"
"wipe_tower_wall_type": "rib",
"gap_fill_target": "topbottom"
}
@@ -1,32 +0,0 @@
{
"type": "process",
"name": "0.18 Standard @Snapmaker U1 (0.6 nozzle)",
"inherits": "fdm_process_U1_0.18_nozzle_0.6",
"from": "system",
"setting_id": "GP028",
"instantiation": "true",
"description": "Compared with the default profile of a 0.6 mm nozzle, it has a smaller layer height, and results in less apparent layer lines and higher printing quality, but longer printing time.",
"elefant_foot_compensation": "0.15",
"smooth_coefficient": "150",
"overhang_totally_speed": "50",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filter_out_gap_fill": "1",
"gap_fill_target": "topbottom",
"internal_bridge_speed": "100%",
"layer_height": "0.25",
"prime_tower_brim_width": "5",
"prime_tower_width": "30",
"prime_volume": "41",
"seam_gap": "15%",
"wipe_tower_extra_rib_length": "8",
"wipe_tower_extra_spacing": "120%",
"wipe_tower_wall_type": "rib",
"enable_arc_fitting": "0",
"ooze_prevention": "1",
"precise_outer_wall": "0",
"slowdown_for_curled_perimeters": "0",
"standby_temperature_delta": "-150",
"support_type": "tree(auto)"
}
+1 -1
View File
@@ -6,4 +6,4 @@
- Feature: Added a new model entry
### Bugfix
Fixed the issue where devices would automatically disconnect when connected over Lan network
Fixed the issue where devices would automatically disconnect when connected over Lan network
@@ -38,7 +38,7 @@
<color type="primary" scheme_preference="light">#009688</color>
</branding>
<releases>
<release version="2.2.4" date="2025-12-26">
<release version="2.2.4" date="2026-3-4">
<description>
<p>Version 2.2.4 release with improvements and bug fixes.</p>
</description>
+7
View File
@@ -5,6 +5,13 @@
#include <sstream>
static std::atomic<bool> isAgreeSlice(true);
static std::atomic<bool> g_sentry_initialized(false);
bool get_sentry_flags() { return g_sentry_initialized; }
void set_sentry_flags(bool flags) { g_sentry_initialized = flags; }
bool get_privacy_policy()
{
return isAgreeSlice;
+3
View File
@@ -63,6 +63,9 @@
//webview bury point
BURY_API bool get_sentry_flags();
BURY_API void set_sentry_flags(bool flags);
BURY_API bool get_privacy_policy();
BURY_API void set_privacy_policy(bool isAgree);
BURY_API std::string get_timestamp_seconds();
+24 -11
View File
@@ -2094,25 +2094,38 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
// brim with chamfer (gradual layer-by-layer reduction)
int loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
// Apply chamfer reduction if feature is enabled and brim width is configured
if (m_wipe_tower_brim_width > 0 && m_prime_tower_brim_chamfer) {
if (!first_layer) {
// Apply brim logic based on chamfer setting
if (m_wipe_tower_brim_width > 0) {
if (first_layer) {
// First layer: always print full brim (loops_num unchanged)
} else if (m_prime_tower_brim_chamfer) {
// Non-first layer + chamfer enabled: apply gradual reduction
// Calculate distance from first layer with tool changes
size_t current_idx = m_layer_info - m_plan.begin();
int dist_to_1st = (int)current_idx - (int)m_first_layer_idx;
// Stop print chamfer if depth changes
bool depth_changed = (m_layer_info->depth != m_plan[m_first_layer_idx].depth);
if (depth_changed) {
// Validate m_first_layer_idx to prevent invalid index access
if (m_first_layer_idx == size_t(-1) || m_first_layer_idx >= m_plan.size()) {
// Invalid first layer index, don't print brim
loops_num = 0;
}
else {
// Limit max chamfer width to configured value
int chamfer_loops_num = (int)(m_prime_tower_brim_chamfer_max_width / spacing);
loops_num = std::min(loops_num, chamfer_loops_num) - dist_to_1st;
// Ensure loops_num doesn't go negative
if (loops_num < 0) loops_num = 0;
// Stop print chamfer if depth changes
bool depth_changed = (m_layer_info->depth != m_plan[m_first_layer_idx].depth);
if (depth_changed) {
loops_num = 0;
} else {
// Limit max chamfer width to configured value
int chamfer_loops_num = (int) (m_prime_tower_brim_chamfer_max_width / spacing);
loops_num = std::min(loops_num, chamfer_loops_num) - dist_to_1st;
// Ensure loops_num doesn't go negative
if (loops_num < 0)
loops_num = 0;
}
}
} else {
// Non-first layer + chamfer disabled: don't print brim (revert to original behavior)
loops_num = 0;
}
}
+2 -2
View File
@@ -5747,7 +5747,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Enable gradual layer-by-layer reduction of the brim around the prime tower. "
"This creates a chamfered/tapered effect, reducing material usage while "
"maintaining first layer adhesion.");
def->mode = comAdvanced;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("prime_tower_brim_chamfer_max_width", coFloat);
@@ -5756,7 +5756,7 @@ void PrintConfigDef::init_fff_params()
"The brim will reduce within this distance. Larger values create a more "
"gradual taper but take more layers to complete.");
def->sidetext = "mm"; // milimeters, don't need translation
def->mode = comAdvanced;
def->mode = comDevelop;
def->min = 0.;
def->set_default_value(new ConfigOptionFloat(4.0));
+72 -15
View File
@@ -50,6 +50,23 @@ namespace Slic3r {
#define SENTRY_KEY_LEVEL "level"
#ifdef _WIN32
// C-style wrapper function for sentry_init to allow use of __try/__except
// This function must be C-style because __try/__except cannot be used in functions
// that require C++ object unwinding (functions with C++ objects that need destructors)
extern "C" {
static int safe_sentry_init(sentry_options_t* options) {
int result = -1;
__try {
result = sentry_init(options);
} __except(EXCEPTION_EXECUTE_HANDLER) {
// Exception occurred during sentry_init
result = -1;
}
return result;
}
}
#endif
static sentry_value_t on_crash_callback(const sentry_ucontext_t* uctx, sentry_value_t event, void* closure)
{
@@ -252,31 +269,66 @@ void initSentryEx()
sentry_options_set_logs_with_attributes(options, true);
// Set release version for symbolication
// This must match the release used when uploading symbols
sentry_options_set_release(options, Snapmaker_VERSION);
bool init_success = false;
sentry_init(options);
sentry_start_session();
#ifdef _WIN32
// Use C-style wrapper function to safely call sentry_init with SEH exception handling
int result = safe_sentry_init(options);
std::cout << "sentry_init returned: " << result << std::endl;
if (result == 0) {
init_success = true;
std::cout << "sentry_init succeeded, init_success set to true" << std::endl;
} else {
std::cout << "Error: sentry_init failed or exception occurred, Sentry initialization failed (result=" << result << ")" << std::endl;
// Exception occurred or sentry_init returned error, sentry_init was not successfully called
// so we need to free options
sentry_options_free(options);
set_sentry_flags(false);
return; // Exit early if initialization fails
}
#else
int result = sentry_init(options);
std::cout << "sentry_init returned: " << result << std::endl;
if (result == 0) {
init_success = true;
std::cout << "sentry_init succeeded, init_success set to true" << std::endl;
} else {
std::cout << "Warning: sentry_init returned non-zero: " << result << std::endl;
}
#endif
sentry_set_tag("snapmaker_version", Snapmaker_VERSION);
// Start session and set tags only if initialization succeeded
if (init_success) {
std::cout << "Starting Sentry session and setting flags..." << std::endl;
sentry_start_session();
set_sentry_flags(true);
sentry_set_tag("snapmaker_version", Snapmaker_VERSION);
std::string flutterVersion = common::get_flutter_version();
if (!flutterVersion.empty())
sentry_set_tag("flutter_version", flutterVersion.c_str());
std::string flutterVersion = common::get_flutter_version();
if (!flutterVersion.empty())
sentry_set_tag("flutter_version", flutterVersion.c_str());
std::string machineID = common::getMachineId();
if (!machineID.empty())
sentry_set_tag("machine_id", machineID.c_str());
std::string machineID = common::getMachineId();
if (!machineID.empty())
sentry_set_tag("machine_id", machineID.c_str());
std::string pcName = common::get_pc_name();
if (!pcName.empty())
sentry_set_tag("pc_name", pcName.c_str());
std::string pcName = common::get_pc_name();
if (!pcName.empty())
sentry_set_tag("pc_name", pcName.c_str());
} else {
set_sentry_flags(false);
}
}
}
void exitSentryEx()
{
sentry_close();
{
if (get_sentry_flags()) {
sentry_close();
set_sentry_flags(false);
}
}
void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel,
const std::string& logContent,
@@ -285,6 +337,11 @@ void sentryReportLogEx(SENTRY_LOG_LEVEL logLevel,
const std::string& logTagValue,
const std::string& logTraceId)
{
// Check if Sentry is initialized before using it
if (!get_sentry_flags()) {
return;
}
if (!get_privacy_policy()) {
return;
}
+10 -4
View File
@@ -1331,10 +1331,16 @@ void GLCanvas3D::reset_volumes()
m_dirty = true;
auto pLater = wxGetApp().plater();
if (pLater && wxGetApp().plater()->get_notification_manager())
{
_set_warning_notification(EWarning::ObjectOutside, false);
if (pLater) {
auto* notification_mgr = pLater->get_notification_manager();
if (notification_mgr) {
// Only update notification if we can safely access Plater's current canvas
// This ensures Plater::priv structure is still valid
auto* canvas = pLater->get_current_canvas3D();
if (canvas) {
_set_warning_notification(EWarning::ObjectOutside, false);
}
}
}
}
+6 -1
View File
@@ -3660,6 +3660,8 @@ if (res) {
load_current_presets();
update_publish_status();
mainframe->refresh_plugin_tips();
m_fltviews.reload_all();
// BBS: remove SLA related message
}
} catch (std::exception &) {
@@ -5976,7 +5978,9 @@ void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_
// so we put it into an inner scope
PreferencesDialog dlg(mainframe, open_on_tab, highlight_option);
dlg.ShowModal();
this->plater_->get_current_canvas3D()->force_set_focus();
GLCanvas3D* canvas = this->plater_->get_current_canvas3D();
if (canvas)
canvas->force_set_focus();
// BBS
//app_layout_changed = dlg.settings_layout_changed();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
@@ -6727,6 +6731,7 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
wxGetApp().sidebar().update_nozzle_settings();
update_publish_status();
mainframe->refresh_plugin_tips();
m_fltviews.reload_all();
// BBS: remove SLA related message
}
auto isAgree = wxGetApp().app_config->get("app", PRIVACY_POLICY_FLAGS);
+1 -1
View File
@@ -903,7 +903,7 @@ public:
}
}
void relead_all() {
void reload_all() {
for (const auto& view : webviews) {
auto ptr = view.first;
wxString new_url = app->get_international_url(view.second);
+20
View File
@@ -330,11 +330,25 @@ bool CheckboxFileDialog::get_checkbox_value() const
}
// GTK requires width >= -1 and height > 0 for gtk_window_resize/set_size_request.
// Use minimum 100 to avoid 0 or negative dimensions from bad config or Intersect().
static const int WINDOW_MIN_WIDTH = 100;
static const int WINDOW_MIN_HEIGHT = 100;
static void clamp_rect_to_minimum_size(wxRect &rect)
{
if (rect.width < WINDOW_MIN_WIDTH)
rect.width = WINDOW_MIN_WIDTH;
if (rect.height < WINDOW_MIN_HEIGHT)
rect.height = WINDOW_MIN_HEIGHT;
}
WindowMetrics WindowMetrics::from_window(wxTopLevelWindow *window)
{
WindowMetrics res;
res.rect = window->GetScreenRect();
res.maximized = window->IsMaximized();
clamp_rect_to_minimum_size(res.rect);
return res;
}
@@ -362,6 +376,7 @@ boost::optional<WindowMetrics> WindowMetrics::deserialize(const std::string &str
WindowMetrics res;
res.rect = wxRect(metrics[0], metrics[1], metrics[2], metrics[3]);
clamp_rect_to_minimum_size(res.rect);
res.maximized = metrics[4] != 0;
return res;
@@ -370,6 +385,11 @@ boost::optional<WindowMetrics> WindowMetrics::deserialize(const std::string &str
void WindowMetrics::sanitize_for_display(const wxRect &screen_rect)
{
rect = rect.Intersect(screen_rect);
// Intersect can yield 0 or negative width/height; GTK asserts on that.
clamp_rect_to_minimum_size(rect);
// Keep within display (e.g. after monitor change)
rect.x = std::max(screen_rect.x, std::min(rect.x, screen_rect.x + screen_rect.width - rect.width));
rect.y = std::max(screen_rect.y, std::min(rect.y, screen_rect.y + screen_rect.height - rect.height));
// Prevent the window from going too far towards the right and/or bottom edge
// It's hardcoded here that the threshold is 80% of the screen size
+1 -1
View File
@@ -16,7 +16,7 @@
#define LOCALHOST_PORT 13618
#define PAGE_HTTP_PORT 13619
#define LOCALHOST_URL "http://localhost:"
#define LOCALHOST_URL "http://127.0.0.1:"
namespace Slic3r { namespace GUI {
+19 -5
View File
@@ -270,6 +270,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
// Load the icon either from the exe, or from the ico file.
SetIcon(main_frame_icon(wxGetApp().get_app_mode()));
#ifdef __WXGTK__
// GTK/X11: set minimum size before any layout so gtk_widget_set_size_request and
// gtk_window_resize never see 0 or negative dimensions (avoids assertion failures).
SetMinSize(wxGetApp().get_min_size());
#endif
// initialize tabpanel and menubar
init_tabpanel();
if (wxGetApp().is_gcode_viewer())
@@ -501,8 +507,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
//FIXME maybe this is useful for __WXGTK3__ as well?
#if __APPLE__
Bind(wxEVT_MOVE, [](wxMoveEvent& event) {
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D();
if (canvas) {
canvas->set_as_dirty();
canvas->request_extra_frame();
}
event.Skip();
});
#endif
@@ -2240,7 +2249,9 @@ static wxMenu* generate_help_menu()
append_menu_item(helpMenu, wxID_ANY, _L("Show Tip of the Day"), _L("Show Tip of the Day"), [](wxCommandEvent&) {
wxGetApp().plater()->get_dailytips()->open();
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D();
if (canvas)
canvas->set_as_dirty();
});
// Report a bug
@@ -2515,8 +2526,11 @@ void MainFrame::init_menubar_as_editor()
auto handle_key_event = [](wxKeyEvent& evt) {
if (wxGetApp().imgui()->update_key_data(evt)) {
wxGetApp().plater()->get_current_canvas3D()->render();
return true;
GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D();
if (canvas) {
canvas->render();
return true;
}
}
return false;
};
+19 -3
View File
@@ -146,8 +146,15 @@ bool MarkdownTip::ShowTip(wxPoint pos, std::string const &tip, std::string const
if (_tipView->GetParent() == this) {
wxSize size = wxDisplay(this).GetClientArea().GetSize();
_requestPos = pos;
// Ensure display size is valid
if (size.y <= 0) size.y = 600;
if (pos.y + this->GetSize().y > size.y)
pos.y = size.y - this->GetSize().y;
{
pos.y = 0;
if( (size.y - this->GetSize().y) > 0)
pos.y = (size.y - this->GetSize().y);
}
this->SetPosition(pos);
if (tipChanged || _hide) {
_hide = false;
@@ -258,11 +265,20 @@ void MarkdownTip::OnTitleChanged(wxWebViewEvent& event)
_lastHeight = height;
height *= 1.25; height += 50;
wxSize size = wxDisplay(this).GetClientArea().GetSize();
// Ensure display size is valid
if (size.y <= 0) size.y = 600;
if (height > size.y)
height = size.y;
wxPoint pos = _requestPos;
if (pos.y + height > size.y)
pos.y = size.y - height;
if (pos.y + height > size.y)
{
pos.y= 0;
if(size.y-height>0)
pos.y = size.y - height;
}
// Ensure height is valid
if (height <= 0) height = 100;
this->SetSize({ 400, (int)height });
this->SetPosition(pos);
}
+94 -34
View File
@@ -1903,11 +1903,14 @@ void Sidebar::update_presets(Preset::Type preset_type)
}
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
if (auto printer_structure_opt = printer_preset.config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure")) {
wxGetApp().plater()->get_current_canvas3D()->get_arrange_settings().align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3);
GLCanvas3D* canvas = wxGetApp().plater()->get_current_canvas3D();
if (canvas) {
if (auto printer_structure_opt = printer_preset.config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure")) {
canvas->get_arrange_settings().align_to_y_axis = (printer_structure_opt->value == PrinterStructure::psI3);
}
else
canvas->get_arrange_settings().align_to_y_axis = false;
}
else
wxGetApp().plater()->get_current_canvas3D()->get_arrange_settings().align_to_y_axis = false;
break;
}
@@ -2749,9 +2752,12 @@ void Sidebar::update_ui_from_settings()
// BBS
//p->object_manipulation->update_ui_from_settings();
// update Cut gizmo, if it's open
p->plater->canvas3D()->update_gizmos_on_off_state();
p->plater->set_current_canvas_as_dirty();
p->plater->get_current_canvas3D()->request_extra_frame();
GLCanvas3D* canvas = p->plater->canvas3D();
if (canvas) {
canvas->update_gizmos_on_off_state();
p->plater->set_current_canvas_as_dirty();
p->plater->get_current_canvas3D()->request_extra_frame();
}
#if 0
p->object_list->apply_volumes_order();
#endif
@@ -5671,7 +5677,13 @@ Selection& Plater::priv::get_selection()
Selection& Plater::priv::get_curr_selection()
{
return get_current_canvas3D()->get_selection();
GLCanvas3D* canvas = get_current_canvas3D();
if (!canvas) {
// During destruction, return a reference to a static empty selection
static Selection empty_selection;
return empty_selection;
}
return canvas->get_selection();
}
int Plater::priv::get_selected_object_idx() const
@@ -5701,10 +5713,15 @@ void Plater::priv::selection_changed()
}
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
assemble_view->render();
} else {
view3D->render();
GLCanvas3D* canvas = get_current_canvas3D();
if (canvas) {
if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
if (assemble_view)
assemble_view->render();
} else {
if (view3D)
view3D->render();
}
}
}
@@ -6473,7 +6490,10 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
return false;
}
wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
GLCanvas3D* canvas = q->get_current_canvas3D();
if (!canvas)
return false;
wxBusyInfo info(_L("Replace from:") + " " + from_u8(path), canvas->get_wxglcanvas());
if (!snapshot.empty())
q->take_snapshot(snapshot);
@@ -6770,7 +6790,13 @@ void Plater::priv::reload_from_disk()
if (color_dlg.ShowModal() != wxID_OK) { filament_ids.clear(); }
};
wxBusyCursor wait;
wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
GLCanvas3D* canvas = q->get_current_canvas3D();
if (!canvas)
{
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "canvas is nullptr";
continue;
}
wxBusyInfo info(_L("Reload from:") + " " + from_u8(path), canvas->get_wxglcanvas());
Model new_model;
try
@@ -8839,16 +8865,26 @@ void Plater::priv::set_current_canvas_as_dirty()
GLCanvas3D* Plater::priv::get_current_canvas3D(bool exclude_preview)
{
if (current_panel == view3D)
// During destruction, these pointers may be null or point to destroyed objects
// Add null checks to prevent crashes during shutdown
if (current_panel == view3D) {
if (view3D)
return view3D->get_canvas3d();
}
else if (!exclude_preview && (current_panel == preview)) {
if (preview)
return preview->get_canvas3d();
}
else if (current_panel == assemble_view) {
if (assemble_view)
return assemble_view->get_canvas3d();
}
//BBS default set to view3D, but check if it's still valid
if (view3D)
return view3D->get_canvas3d();
else if (!exclude_preview && (current_panel == preview))
return preview->get_canvas3d();
else if (current_panel == assemble_view)
return assemble_view->get_canvas3d();
else //BBS default set to view3D
return view3D->get_canvas3d();
//return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
return nullptr;
}
void Plater::priv::unbind_canvas_event_handlers()
@@ -9386,7 +9422,13 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle");
}
}
const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager();
GLCanvas3D* canvas = get_current_canvas3D();
if (!canvas) {
// During destruction, skip snapshot
return;
}
const GLGizmosManager& gizmos = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager();
if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator)
this->undo_redo_stack().clear();
@@ -9420,7 +9462,9 @@ void Plater::priv::undo()
// BBS: undo-redo until modify record
while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current));
if (it_current == snapshots.begin()) return;
if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
GLCanvas3D* canvas = get_current_canvas3D();
if (!canvas) return;
if (canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction &&
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo &&
it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::LeavingGizmoNoAction &&
@@ -9528,9 +9572,13 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
// Make a copy of the snapshot, undo/redo could invalidate the iterator
const UndoRedo::Snapshot snapshot_copy = *it_snapshot;
// Do the jump in time.
GLCanvas3D* canvas = get_current_canvas3D();
if (!canvas) return;
bool is_assemble = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView;
if (it_snapshot->timestamp < this->undo_redo_stack().active_snapshot_time() ?
this->undo_redo_stack().undo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
this->undo_redo_stack().redo(model, get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
this->undo_redo_stack().undo(model, is_assemble ? assemble_view->get_canvas3d()->get_selection() : this->view3D->get_canvas3d()->get_selection(), is_assemble ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, top_snapshot_data, it_snapshot->timestamp) :
this->undo_redo_stack().redo(model, is_assemble ? assemble_view->get_canvas3d()->get_gizmos_manager() : this->view3D->get_canvas3d()->get_gizmos_manager(), this->partplate_list, it_snapshot->timestamp)) {
if (printer_technology_changed) {
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
AppConfig *app_config = wxGetApp().app_config;
@@ -9608,7 +9656,11 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
{
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear();
GLCanvas3D* canvas = get_current_canvas3D();
if (!canvas) return;
bool is_assemble = canvas->get_canvas_type() == GLCanvas3D::CanvasAssembleView;
is_assemble ? assemble_view->get_canvas3d()->get_selection().clear() : this->view3D->get_canvas3d()->get_selection().clear();
// Update volumes from the deserializd model, always stop / update the background processing (for both the SLA and FFF technologies).
this->update((unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE | (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE);
// Release old snapshots if the memory allocated is excessive. This may remove the top most snapshot if jumping to the very first snapshot.
@@ -9617,10 +9669,10 @@ void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bo
// triangle meshes may have gotten released from the scene or the background processing, therefore now being calculated into the Undo / Redo stack size.
this->undo_redo_stack().release_least_recently_used();
//YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
is_assemble ?
assemble_view->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances) :
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack().selection_deserialized().mode), this->undo_redo_stack().selection_deserialized().volumes_and_instances);
get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
is_assemble ?
assemble_view->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot) :
this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(snapshot);
@@ -11299,7 +11351,9 @@ void Plater::load_gcode(const wxString& filename)
p->preview->reload_print(false, m_only_gcode);
wxGetApp().mainframe->select_tab(MainFrame::tpPreview);
p->set_current_panel(p->preview, true);
p->get_current_canvas3D()->render();
GLCanvas3D* canvas = p->get_current_canvas3D();
if (canvas)
canvas->render();
//p->notification_manager->bbl_show_plateinfo_notification(into_u8(_L("Preview only mode for gcode file.")));
wxBusyCursor wait;
@@ -12299,7 +12353,9 @@ void Plater::set_selected_visible(bool visible)
Plater::TakeSnapshot snapshot(this, "Set Selected Objects Visible in AssembleView");
get_ui_job_worker().cancel_all();
p->get_current_canvas3D()->set_selected_visible(visible);
GLCanvas3D* canvas = p->get_current_canvas3D();
if (canvas)
canvas->set_selected_visible(visible);
}
@@ -12319,7 +12375,9 @@ void Plater::remove_selected()
//BBS delete current selected
// p->view3D->delete_selected();
p->get_current_canvas3D()->delete_selected();
GLCanvas3D* canvas = p->get_current_canvas3D();
if (canvas)
canvas->delete_selected();
}
void Plater::increase_instances(size_t num)
@@ -14592,7 +14650,9 @@ void Plater::changed_object(ModelObject &object){
p->schedule_background_process();
// Check outside bed
get_current_canvas3D()->requires_check_outside_state();
GLCanvas3D* canvas = get_current_canvas3D();
if (canvas)
canvas->requires_check_outside_state();
}
void Plater::changed_object(int obj_idx)
+3
View File
@@ -205,6 +205,9 @@ void PresetComboBox::update_selection()
// A workaround for a set of issues related to text fitting into gtk widgets:
#if defined(__WXGTK20__) || defined(__WXGTK3__)
// Guard: m_widget may not be a GtkCellLayout (e.g. not yet realized, or GtkComboBoxText in Flatpak/GTK3)
if (!m_widget || !GTK_IS_CELL_LAYOUT(m_widget))
return;
GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_widget));
// 'cells' contains the GtkCellRendererPixBuf for the icon,
+5 -1
View File
@@ -1523,7 +1523,11 @@ void SSWCP_Instance::update_filament_info(const json& objects, bool send_message
// 名称特殊处理
if (type == "TPU") {
name = vendor + " " + type;
if (sub_type == "95A HF") {
name = vendor + " " + type + ((sub_type != "NONE" && sub_type != "") ? " " + sub_type : "");
} else {
name = vendor + " " + type;
}
} else if (sub_type == "Support") {
name = vendor + " Support" + " For " + type;
} else {
+1 -1
View File
@@ -496,7 +496,7 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
}
}
wxGetApp().fltviews().relead_all();
wxGetApp().fltviews().reload_all();
}
this->EndModal(wxID_OK);
+14 -1
View File
@@ -368,7 +368,12 @@ void DropDown::messureSize()
wxWindow::SetSize(szContent);
#ifdef __WXGTK__
// Gtk has a wrapper window for popup widget
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
// Fix for GNOME Platform 48 X11 backend: ensure size is valid before calling gtk_window_resize
int gtk_width = szContent.x;
int gtk_height = szContent.y;
if (gtk_width <= 0) gtk_width = 100;
if (gtk_height <= 0) gtk_height = 100;
gtk_window_resize(GTK_WINDOW(m_widget), gtk_width, gtk_height);
#endif
need_sync = false;
}
@@ -384,6 +389,10 @@ void DropDown::autoPosition()
size = rowSize;
size.y *= std::min((size_t)15, texts.size());
size.y += texts.size() > 15 ? rowSize.y / 2 : 0;
#ifdef __WXGTK__
if (size.x < 1) size.x = 1;
if (size.y < 1) size.y = 1;
#endif
if (size != GetSize()) {
wxWindow::SetSize(size);
offset = wxPoint();
@@ -396,6 +405,10 @@ void DropDown::autoPosition()
if (GetPosition().y + size.y + 10 > drect.GetBottom()) {
if (use_content_width && texts.size() <= 15) size.x += 6;
size.y = drect.GetBottom() - GetPosition().y - 10;
#ifdef __WXGTK__
if (size.y < 1) size.y = 1;
if (size.x < 1) size.x = 1;
#endif
wxWindow::SetSize(size);
if (selection >= 0) {
if (offset.y + rowSize.y * (selection + 1) > size.y)
+1 -1
View File
@@ -30,7 +30,7 @@ class DropDown : public PopupWindow
wxSize textSize;
wxSize iconSize;
wxSize rowSize;
wxSize rowSize{100, 30}; // Initialize with default values
StateHandler state_handler;
StateColor text_color;
+15 -2
View File
@@ -17,6 +17,11 @@
#endif
#include "sentry_wrapper/SentryWrapper.hpp"
#if defined(__linux__)
#include <mutex>
#endif
#ifdef __WIN32__
#include <WebView2.h>
#include <Shellapi.h>
@@ -275,10 +280,18 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
// And the memory: file system
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
#else
// With WKWebView handlers need to be registered before creation
// Handlers must be registered before Create(). Linux (WebKit2): scheme is process-global, register once to avoid "Cannot register URI scheme ... more than once".
// macOS (WKWebView): scheme is per-view, each WebView needs its own handlers.
#if defined(__linux__)
static std::once_flag s_wxfs_memory_handlers_once;
std::call_once(s_wxfs_memory_handlers_once, [webView]() {
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("wxfs")));
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
});
#else
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("wxfs")));
// And the memory: file system
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
#endif
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
webView->SetUserAgent(wxString::Format("SM-Slicer/v%s (%s) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)", SLIC3R_VERSION,
Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light"));