Compare commits

..

1 Commits

Author SHA1 Message Date
Ian Chua
14537aac9e fix: load 3mf project after sync 2026-05-25 16:57:26 +08:00
4 changed files with 94 additions and 48 deletions

View File

@@ -338,21 +338,7 @@ void FilamentGroupPopup::OnRadioBtn(int idx)
}
}
void FilamentGroupPopup::OnTimer(wxTimerEvent &event)
{
#if __APPLE__
// On macOS, when moving cursor from slice button to this popup window,
// the popup window entering event is triggered first, then the slice button
// leaving event got triggered. So the timer is stopped first, then started
// again, causing the popup being dismissed immediately.
// To fix this, we check if cursor is still inside the popup window before
// dismissing.
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
#endif
Dismiss();
}
void FilamentGroupPopup::OnTimer(wxTimerEvent &event) { Dismiss(); }
void FilamentGroupPopup::Dismiss() {
m_active = false;

View File

@@ -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<OrcaCloudServiceAgent>(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

View File

@@ -322,6 +322,7 @@ private:
boost::thread m_sync_update_thread;
std::shared_ptr<int> m_user_sync_token;
std::atomic<bool> m_restart_sync_pending {false};
std::vector<std::string> 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();

View File

@@ -2448,7 +2448,7 @@ void TabPrint::build()
optgroup->append_single_option_line("sparse_infill_density", "strength_settings_infill#sparse-infill-density");
optgroup->append_single_option_line("fill_multiline", "strength_settings_infill#fill-multiline");
optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern");
optgroup->append_single_option_line("gyroid_optimized", "strength_settings_patterns#gyroid-optimized");
optgroup->append_single_option_line("gyroid_optimized", "strength_settings_patterns#gyroid_optimized");
optgroup->append_single_option_line("infill_direction", "strength_settings_infill#direction");
optgroup->append_single_option_line("sparse_infill_rotate_template", "strength_settings_infill_rotation_template_metalanguage");
optgroup->append_single_option_line("skin_infill_density", "strength_settings_patterns#locked-zag");