diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6b30dfa9cd..c6ced2e4c4 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -737,36 +737,50 @@ void GUI_App::post_init() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", init with input files, size %1%, input_gcode %2%") %this->init_params->input_files.size() %this->init_params->input_gcode; - switch_to_3d = true; + bool load_immediately = true; + if (app_config->get("sync_user_preset") == "true" && m_agent && m_agent->is_user_login() + && !this->init_params->input_gcode + && !(this->init_params->input_files.size() == 1 && is_supported_open_protocol(this->init_params->input_files.front()))) { + // Defer loading until after cloud sync completes, so project settings + // from the 3MF override synced presets instead of the reverse. + m_pending_input_files = std::move(this->init_params->input_files); + this->init_params->input_files.clear(); + load_immediately = false; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": deferring input file load until after cloud sync"; + } - const auto first_url = this->init_params->input_files.front(); - if (this->init_params->input_files.size() == 1 && is_supported_open_protocol(first_url)) { - start_download(first_url); - m_open_method = "url"; - } else { - if (this->init_params->input_gcode) { - mainframe->select_tab(size_t(MainFrame::tp3DEditor)); - plater_->select_view_3D("3D"); - this->plater()->load_gcode(from_u8(this->init_params->input_files.front())); - m_open_method = "gcode"; + if (load_immediately) { + switch_to_3d = true; + + const auto first_url = this->init_params->input_files.front(); + if (this->init_params->input_files.size() == 1 && is_supported_open_protocol(first_url)) { + start_download(first_url); + m_open_method = "url"; } else { - mainframe->select_tab(size_t(MainFrame::tp3DEditor)); - plater_->select_view_3D("3D"); - wxArrayString input_files; - for (auto& file : this->init_params->input_files) { - input_files.push_back(wxString::FromUTF8(file)); - } - this->plater()->set_project_filename(_L("Untitled")); - this->plater()->load_files(input_files); - try { - if (!input_files.empty()) { - std::string file_path = input_files.front().ToStdString(); - std::filesystem::path path(file_path); - m_open_method = "file_" + path.extension().string(); + if (this->init_params->input_gcode) { + mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + plater_->select_view_3D("3D"); + this->plater()->load_gcode(from_u8(this->init_params->input_files.front())); + m_open_method = "gcode"; + } else { + mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + plater_->select_view_3D("3D"); + wxArrayString input_files; + for (auto& file : this->init_params->input_files) { + input_files.push_back(wxString::FromUTF8(file)); + } + this->plater()->set_project_filename(_L("Untitled")); + this->plater()->load_files(input_files); + try { + if (!input_files.empty()) { + std::string file_path = input_files.front().ToStdString(); + std::filesystem::path path(file_path); + m_open_method = "file_" + path.extension().string(); + } + } catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", file path exception!"; + m_open_method = "file"; } - } catch (...) { - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", file path exception!"; - m_open_method = "file"; } } } @@ -887,12 +901,9 @@ void GUI_App::post_init() show_network_plugin_download_dialog(false); } - // Start preset sync after project opened, otherwise we could have preset change during project opening which could cause crash + // Start preset sync. When input files are present, loading is deferred so + // the sync runs first and the 3MF project settings override synced defaults. if (app_config->get("sync_user_preset") == "true") { - // BBS loading user preset - // Always async, not such startup step - // BOOST_LOG_TRIVIAL(info) << "Loading user presets..."; - // scrn->SetText(_L("Loading user presets...")); if (m_agent) { start_sync_user_preset(); } @@ -901,6 +912,12 @@ void GUI_App::post_init() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " sync_user_preset: false"; } + // If input files were deferred but sync wasn't started (not logged in, etc.), load them now + if (!m_pending_input_files.empty() && !m_user_sync_token) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": sync not started, loading deferred input files directly"; + CallAfter([this]() { load_pending_input_files(); }); + } + // The extra CallAfter() is needed because of Mac, where this is the only way // to popup a modal dialog on start without screwing combo boxes. // This is ugly but I honestly found no better way to do it. @@ -5895,6 +5912,38 @@ void GUI_App::remove_user_presets() } } +void GUI_App::load_pending_input_files() +{ + if (m_pending_input_files.empty() || is_closing()) return; + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": loading " << m_pending_input_files.size() << " deferred input file(s)"; + + mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + plater_->select_view_3D("3D"); + + wxArrayString input_files; + for (auto& file : m_pending_input_files) + input_files.push_back(wxString::FromUTF8(file)); + + this->plater()->set_project_filename(_L("Untitled")); + this->plater()->load_files(input_files); + + try { + if (!input_files.empty()) { + std::string file_path = input_files.front().ToStdString(); + std::filesystem::path path(file_path); + m_open_method = "file_" + path.extension().string(); + } + } catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", file path exception!"; + m_open_method = "file"; + } + + plater_->trigger_restore_project(1); + m_pending_input_files.clear(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": deferred input files loaded"; +} + // Check if the user's OrcaCloud profile directory is empty and offer to migrate // existing profiles from the default or BambuCloud user folder. // Returns true if migration was performed, false otherwise. @@ -6657,6 +6706,15 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) if (ret == 0 && m_agent && !t.expired()) reload_settings(); + // After initial sync cycle, load any input files that were deferred + // so the 3MF project settings override the freshly-synced cloud defaults. + if (!m_pending_input_files.empty()) { + CallAfter([this]() { + if (!m_pending_input_files.empty()) + load_pending_input_files(); + }); + } + // For orca specific syncing auto orca_agent = std::dynamic_pointer_cast(m_agent->get_cloud_agent()); int tick_tock = -1, sync_count = 0; // tick_tock = -1 to immediately run sync the frist time this thread runs diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 5b6bcc6581..6ae11ca29f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -322,6 +322,7 @@ private: boost::thread m_sync_update_thread; std::shared_ptr m_user_sync_token; std::atomic m_restart_sync_pending {false}; + std::vector m_pending_input_files; // input files deferred until after cloud sync bool m_is_dark_mode{ false }; bool m_adding_script_handler { false }; bool m_side_popup_status{false}; @@ -521,6 +522,7 @@ public: void push_notification(const MachineObject* obj, wxString msg, wxString title = wxEmptyString, UserNotificationStyle style = UserNotificationStyle::UNS_NORMAL); void reload_settings(); void remove_user_presets(); + void load_pending_input_files(); bool maybe_migrate_user_presets_on_login();