From 1dc8ed5610a35ec3dec5d6313b20b1ddff84b9c9 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 1 Feb 2023 10:02:18 +0100 Subject: [PATCH 01/13] Additional checks on filename creation in downloader --- src/slic3r/GUI/DownloaderFileGet.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 0a1e7ce25..ee407afdd 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -137,13 +137,30 @@ void FileGet::priv::get_perform() std::string extension = boost::filesystem::extension(dest_path); std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size()); std::string final_filename = just_filename; - - size_t version = 0; - while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download"))) + // Find unsed filename + try { + size_t version = 0; + while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download"))) + { + ++version; + if (version > 999) { + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR); + evt->SetString(GUI::format_wxstr(L"Failed to find suitable filename. Last name: %1%." , (m_dest_folder / (final_filename + extension)).string())); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + return; + } + final_filename = GUI::format("%1%(%2%)", just_filename, std::to_string(version)); + } + } catch (const boost::filesystem::filesystem_error& e) { - ++version; - final_filename = just_filename + "(" + std::to_string(version) + ")"; + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR); + evt->SetString(e.what()); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + return; } + m_filename = final_filename + extension; m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download"); From ee0871880f0ed5ab34f64812649b25f8b229f727 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 3 Feb 2023 16:43:26 +0100 Subject: [PATCH 02/13] archive dialog dark mode fix --- src/slic3r/GUI/FileArchiveDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 2b861692a..7e61e59a0 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -267,6 +267,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); this->SetMinSize(wxSize(80 * em, 30 * em)); this->SetSizer(topSizer); + + wxGetApp().UpdateDlgDarkUI(this, true); } void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) From 41ab733cd896d794c86865c043baad5d01d640c6 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Feb 2023 15:31:01 +0100 Subject: [PATCH 03/13] Refactoring of DownloaderUtils (remove including ConfigWizard_private.hpp)) --- src/slic3r/GUI/ConfigWizard.cpp | 18 ++++++------- src/slic3r/GUI/ConfigWizard.hpp | 30 +++++++++++++++++++++ src/slic3r/GUI/ConfigWizard_private.hpp | 36 +------------------------ src/slic3r/GUI/GUI_App.cpp | 5 ++-- src/slic3r/GUI/GUI_App.hpp | 4 +-- src/slic3r/GUI/Preferences.cpp | 2 +- src/slic3r/GUI/Preferences.hpp | 2 +- 7 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 1cf647ed7..0fb934996 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1315,10 +1315,12 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } + + namespace DownloaderUtils { +namespace { #ifdef _WIN32 - wxString get_downloads_path() { wxString ret; @@ -1330,7 +1332,6 @@ namespace DownloaderUtils CoTaskMemFree(path); return ret; } - #elif __APPLE__ wxString get_downloads_path() { @@ -1348,9 +1349,8 @@ namespace DownloaderUtils } return wxString(); } - #endif - + } Worker::Worker(wxWindow* parent) : wxBoxSizer(wxHORIZONTAL) , m_parent(parent) @@ -1432,16 +1432,16 @@ PageDownloader::PageDownloader(ConfigWizard* parent) ))); #endif - box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->downloader->allow(event.IsChecked()); }); + box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->m_downloader->allow(event.IsChecked()); }); - downloader = new DownloaderUtils::Worker(this); - append(downloader); - downloader->allow(box_allow_value); + m_downloader = new DownloaderUtils::Worker(this); + append(m_downloader); + m_downloader->allow(box_allow_value); } bool PageDownloader::on_finish_downloader() const { - return downloader->on_finish(); + return m_downloader->on_finish(); } bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/) diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index 88d2e2d7c..4520dbb8f 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -14,6 +14,36 @@ class PresetUpdater; namespace GUI { +namespace DownloaderUtils { + class Worker : public wxBoxSizer + { + wxWindow* m_parent{ nullptr }; + wxTextCtrl* m_input_path{ nullptr }; + bool downloader_checked{ false }; +#ifdef __linux__ + bool perform_registration_linux{ false }; +#endif // __linux__ + + void deregister(); + + public: + Worker(wxWindow* parent); + ~Worker() {} + + void allow(bool allow_) { downloader_checked = allow_; } + bool is_checked() const { return downloader_checked; } + wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); } + + void set_path_name(wxString name); + void set_path_name(const std::string& name); + + bool on_finish(); + bool perform_register(const std::string& path_override = {}); +#ifdef __linux__ + bool get_perform_registration_linux() { return perform_registration_linux; } +#endif // __linux__ + }; +} class ConfigWizard: public DPIDialog { diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index c7c6e5152..832bef46c 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -418,44 +418,10 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; -namespace DownloaderUtils { - wxString get_downloads_path(); - -class Worker : public wxBoxSizer -{ - wxWindow* m_parent {nullptr}; - wxTextCtrl* m_input_path {nullptr}; - bool downloader_checked {false}; -#ifdef __linux__ - bool perform_registration_linux { false }; -#endif // __linux__ - - void deregister(); - -public: - Worker(wxWindow* parent); - ~Worker(){} - - void allow(bool allow_) { downloader_checked = allow_; } - bool is_checked() const { return downloader_checked; } - wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); } - - void set_path_name(wxString name); - void set_path_name(const std::string& name); - - bool on_finish(); - bool perform_register(const std::string& path_override = {}); -#ifdef __linux__ - bool get_perform_registration_linux() { return perform_registration_linux; } -#endif // __linux__ -}; - -} - struct PageDownloader : ConfigWizardPage { - DownloaderUtils::Worker* downloader{ nullptr }; + DownloaderUtils::Worker* m_downloader { nullptr }; PageDownloader(ConfigWizard* parent); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a4cfc0cb9..6d49e22f1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -79,7 +79,6 @@ #include "DesktopIntegrationDialog.hpp" #include "SendSystemInfoDialog.hpp" #include "Downloader.hpp" -#include "ConfigWizard_private.hpp" #include "BitmapCache.hpp" #include "Notebook.hpp" @@ -3081,8 +3080,8 @@ void GUI_App::show_downloader_registration_dialog() ), SLIC3R_APP_NAME, SLIC3R_VERSION) , true, wxYES_NO); if (msg.ShowModal() == wxID_YES) { - auto downloader = new DownloaderUtils::Worker(nullptr); - downloader->perform_register(app_config->get("url_downloader_dest")); + auto downloader_worker = new DownloaderUtils::Worker(nullptr); + downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ if (downloader->get_perform_registration_linux()) DesktopIntegrationDialog::perform_desktop_integration(true); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 1a58bd809..3f8d47fd7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -306,8 +306,8 @@ public: Plater* plater(); const Plater* plater() const; Model& model(); - NotificationManager * notification_manager(); - GalleryDialog * gallery_dialog(); + NotificationManager* notification_manager(); + GalleryDialog * gallery_dialog(); Downloader* downloader(); // Parameters extracted from the command line to be passed to GUI after initialization. diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 40c5314ee..0b17043ef 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -10,7 +10,7 @@ #include "ButtonsDescription.hpp" #include "OG_CustomCtrl.hpp" #include "GLCanvas3D.hpp" -#include "ConfigWizard_private.hpp" +#include "ConfigWizard.hpp" #include diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 15a957692..4ae93f6f9 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -59,7 +59,7 @@ class PreferencesDialog : public DPIDialog wxColourPickerCtrl* m_mode_advanced { nullptr }; wxColourPickerCtrl* m_mode_expert { nullptr }; - DownloaderUtils::Worker* downloader{ nullptr }; + DownloaderUtils::Worker* downloader { nullptr }; wxBookCtrlBase* tabs {nullptr}; From 74c34a311556ba0192b2995a1ee78d32a5242247 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Feb 2023 16:57:05 +0100 Subject: [PATCH 04/13] Archive Dialog: Fix of darkmode and resizing followup of cff356ba35aaa9d9fb7780880f60cf1a3e266187 --- src/slic3r/GUI/FileArchiveDialog.cpp | 33 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 7e61e59a0..783c4be3c 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -172,16 +172,20 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) , m_selected_paths (selected_paths) { +#ifdef _WIN32 + wxGetApp().UpdateDarkUI(this); +#else + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif + int em = em_unit(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em)); m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); m_avc->AppendTextColumn("filename", 1); - std::vector> stack; std::function >&, size_t)> reduce_stack = [] (std::vector>& stack, size_t size) { @@ -233,6 +237,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar } // sorting files will help adjust_stack function to not create multiple same folders std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); }); + size_t entry_count = 0; for (const boost::filesystem::path& path : filtered_entries) { std::shared_ptr parent(nullptr); @@ -242,33 +247,37 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + entry_count++; } } + if (entry_count == 1) + on_all_button(); + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); - wxButton* btn_all = new wxButton(this, wxID_ANY, "All"); + wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All")); btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); }); - btn_sizer->Add(btn_all, 0, wxLeft); + btn_sizer->Add(btn_all, 0); - wxButton* btn_none = new wxButton(this, wxID_ANY, "None"); + wxButton* btn_none = new wxButton(this, wxID_ANY, _L("None")); btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); }); - btn_sizer->Add(btn_none, 0, wxLeft); + btn_sizer->Add(btn_none, 0, wxLEFT, em); btn_sizer->AddStretchSpacer(); - wxButton* btn_run = new wxButton(this, wxID_OK, "Open"); + wxButton* btn_run = new wxButton(this, wxID_OK, _L("Open")); btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); }); - btn_sizer->Add(btn_run, 0, wxRIGHT); + btn_sizer->Add(btn_run, 0, wxRIGHT, em); - wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, "Cancel"); + wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, _L("Cancel")); cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); }); - btn_sizer->Add(cancel_btn, 0, wxRIGHT); + btn_sizer->Add(cancel_btn, 0, wxRIGHT, em); topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10); topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); - this->SetMinSize(wxSize(80 * em, 30 * em)); this->SetSizer(topSizer); - wxGetApp().UpdateDlgDarkUI(this, true); + for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") }) + wxGetApp().UpdateDarkUI(static_cast(FindWindowByLabel(id, this))); } void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) From 09b4a88f18f1e94e65f29c6e5b5d475356455375 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 8 Feb 2023 09:41:20 +0100 Subject: [PATCH 05/13] MacOS notification - URL downloader not allowed --- src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/NotificationManager.hpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6d49e22f1..b64559362 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2889,6 +2889,7 @@ void GUI_App::MacOpenURL(const wxString& url) { if (app_config && !app_config->get_bool("downloader_url_registered")) { + notification_manager()->push_notification(NotificationType::URLNotRegistered); BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url; return; } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index b3a39a936..778f99bfa 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -120,7 +120,9 @@ enum class NotificationType // Short meesage to fill space between start and finish of export ExportOngoing, // Progressbar of download from prusaslicer:// url - URLDownload + URLDownload, + // MacOS specific - PS comes forward even when downloader is not allowed + URLNotRegistered, }; class NotificationManager @@ -916,6 +918,16 @@ private: {NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10, _u8L("Undo desktop integration failed.") }, {NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") }, + {NotificationType::URLNotRegistered + , NotificationLevel::RegularNotificationLevel + , 10 + , _u8L("PrusaSlicer recieved a download request from Printables.com, but it's not allowed. You can allow it") + , _u8L("here.") + , [](wxEvtHandler* evnthndlr) { + wxGetApp().open_preferences("downloader_url_registered", "Other"); + return true; + } }, + //{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { // wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, //{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") }, From 80c92d1331331b159a102013f0cd8437acc22807 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 8 Feb 2023 10:46:04 +0100 Subject: [PATCH 06/13] missing includes and typos --- src/slic3r/GUI/ConfigWizard.cpp | 6 +++--- src/slic3r/GUI/ConfigWizard.hpp | 4 +++- src/slic3r/GUI/ConfigWizard_private.hpp | 2 -- src/slic3r/GUI/GUI_App.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 0fb934996..b45d220b8 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3035,9 +3035,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese #ifdef __linux__ // Desktop integration on Linux - BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->downloader->get_perform_registration_linux(); - if (page_welcome->integrate_desktop() || page_downloader->downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(page_downloader->downloader->get_perform_registration_linux()); + BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux(); + if (page_welcome->integrate_desktop() || page_downloader->m_downloader->get_perform_registration_linux()) + DesktopIntegrationDialog::perform_desktop_integration(page_downloader->m_downloader->get_perform_registration_linux()); #endif // Decide whether to create snapshot based on run_reason and the reset profile checkbox diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index 4520dbb8f..dcd0297ae 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "GUI_Utils.hpp" @@ -17,7 +19,7 @@ namespace GUI { namespace DownloaderUtils { class Worker : public wxBoxSizer { - wxWindow* m_parent{ nullptr }; + wxWindow* m_parent{ nullptr }; wxTextCtrl* m_input_path{ nullptr }; bool downloader_checked{ false }; #ifdef __linux__ diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 832bef46c..2dc2c2e23 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -10,12 +10,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b64559362..cdebbb401 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3084,7 +3084,7 @@ void GUI_App::show_downloader_registration_dialog() auto downloader_worker = new DownloaderUtils::Worker(nullptr); downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ - if (downloader->get_perform_registration_linux()) + if (downloader_worker->get_perform_registration_linux()) DesktopIntegrationDialog::perform_desktop_integration(true); #endif // __linux__ } else { From c447fd525501667948dd6d14c7b82e595f535826 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:18:02 +0100 Subject: [PATCH 07/13] Archive Dialog minimal size --- src/slic3r/GUI/FileArchiveDialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 783c4be3c..e26e152c5 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -275,6 +275,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10); topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); this->SetSizer(topSizer); + SetMinSize(wxSize(40 * em, 30 * em)); for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") }) wxGetApp().UpdateDarkUI(static_cast(FindWindowByLabel(id, this))); @@ -288,9 +289,8 @@ void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) //for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn }) // if (btn) btn->msw_rescale(); - const wxSize& size = wxSize(70 * em, 30 * em); - SetMinSize(size); - + const wxSize& size = wxSize(45 * em, 40 * em); + SetSize(size); //m_tree->Rescale(em); Fit(); From 0ccea3bed915c1c02f39081e337271f0561112bc Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:47:16 +0100 Subject: [PATCH 08/13] Archive dialog - size of left column --- src/slic3r/GUI/FileArchiveDialog.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index e26e152c5..7337258cb 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -182,8 +182,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em)); - m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); + m_avc = new ArchiveViewCtrl(this, wxSize(45 * em, 30 * em)); + wxDataViewColumn* toggle_column = m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); m_avc->AppendTextColumn("filename", 1); std::vector> stack; @@ -238,11 +238,12 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar // sorting files will help adjust_stack function to not create multiple same folders std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); }); size_t entry_count = 0; + size_t depth = 1; for (const boost::filesystem::path& path : filtered_entries) { std::shared_ptr parent(nullptr); - adjust_stack(path, stack); + depth = std::max(depth, adjust_stack(path, stack)); if (!stack.empty()) parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files @@ -253,6 +254,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar if (entry_count == 1) on_all_button(); + toggle_column->SetWidth((4 + depth) * em); + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All")); From e2762bd7d74cae55f2febd63919ff86d50f3b930 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:41:00 +0100 Subject: [PATCH 09/13] Desktop undo registration wont undo downloader registration --- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index a37b76459..9a71e51b2 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -435,6 +435,7 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download "Type=Application\n" "MimeType=x-scheme-handler/prusaslicer;\n" "StartupNotify=false\n" + "NoDisplay=true\n" , name_suffix, version_suffix, excutable_path, version_suffix); // desktop file for downloader as part of main app @@ -487,12 +488,7 @@ void DesktopIntegrationDialog::undo_desktop_intgration() std::remove(path.c_str()); } } - // URL Protocol - path = std::string(app_config->get("desktop_integration_URL_path")); - if (!path.empty()) { - BOOST_LOG_TRIVIAL(debug) << "removing " << path; - std::remove(path.c_str()); - } + // URL Protocol - removed only by undo_downloader_registration now wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess); } From e4ff09a59ffb18e35c3e562baff3ecbbbaa6cd7e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 16 Feb 2023 11:20:17 +0100 Subject: [PATCH 10/13] divide desktop integration and downloader desktop integration --- src/slic3r/GUI/ConfigWizard.cpp | 6 +- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 193 ++++++++++++++++---- src/slic3r/GUI/DesktopIntegrationDialog.hpp | 3 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/Preferences.cpp | 2 +- 5 files changed, 164 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b45d220b8..35292d803 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3036,8 +3036,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese #ifdef __linux__ // Desktop integration on Linux BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux(); - if (page_welcome->integrate_desktop() || page_downloader->m_downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(page_downloader->m_downloader->get_perform_registration_linux()); + if (page_welcome->integrate_desktop()) + DesktopIntegrationDialog::perform_desktop_integration(); + if (page_downloader->m_downloader->get_perform_registration_linux()) + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // Decide whether to create snapshot based on run_reason and the reset profile checkbox diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index 9a71e51b2..62e841153 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -218,9 +218,9 @@ bool DesktopIntegrationDialog::integration_possible() { return true; } -void DesktopIntegrationDialog::perform_desktop_integration(bool perform_downloader) +void DesktopIntegrationDialog::perform_desktop_integration() { - BOOST_LOG_TRIVIAL(debug) << "performing desktop integration. With downloader integration: " << perform_downloader; + BOOST_LOG_TRIVIAL(debug) << "performing desktop integration."; // Path to appimage const char *appimage_env = std::getenv("APPIMAGE"); std::string excutable_path; @@ -423,39 +423,6 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully.")); } } - - if (perform_downloader) - { - std::string desktop_file_downloader = GUI::format( - "[Desktop Entry]\n" - "Name=PrusaSlicer URL Protocol%1%\n" - "Exec=\"%3%\" --single-instance %%u\n" - "Icon=PrusaSlicer%4%\n" - "Terminal=false\n" - "Type=Application\n" - "MimeType=x-scheme-handler/prusaslicer;\n" - "StartupNotify=false\n" - "NoDisplay=true\n" - , name_suffix, version_suffix, excutable_path, version_suffix); - - // desktop file for downloader as part of main app - std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); - if (create_desktop_file(desktop_path, desktop_file_downloader)) { - // save path to desktop file - app_config->set("desktop_integration_URL_path", desktop_path); - // finish registration on mime type - std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); - BOOST_LOG_TRIVIAL(debug) << "system command: " << command; - int r = system(command.c_str()); - BOOST_LOG_TRIVIAL(debug) << "system result: " << r; - - } else { - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create URL Protocol desktop file"; - show_error(nullptr, _L("Performing desktop integration failed - could not create URL Protocol desktop file.")); - return; - } - } - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); } void DesktopIntegrationDialog::undo_desktop_intgration() @@ -488,10 +455,162 @@ void DesktopIntegrationDialog::undo_desktop_intgration() std::remove(path.c_str()); } } - // URL Protocol - removed only by undo_downloader_registration now wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess); } +void DesktopIntegrationDialog::perform_downloader_desktop_integration() +{ + BOOST_LOG_TRIVIAL(debug) << "performing downloader desktop integration."; + // Path to appimage + const char* appimage_env = std::getenv("APPIMAGE"); + std::string excutable_path; + if (appimage_env) { + try { + excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); + } + catch (std::exception&) { + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path."; + show_error(nullptr, _L("Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path.")); + return; + } + } + else { + // not appimage - find executable + excutable_path = boost::dll::program_location().string(); + //excutable_path = wxStandardPaths::Get().GetExecutablePath().string(); + BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path; + if (excutable_path.empty()) + { + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - no executable found."; + show_error(nullptr, _L("Performing downloader desktop integration failed - Could not find executable.")); + return; + } + } + // Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path' + //boost::replace_all(excutable_path, "'", "'\\''"); + excutable_path = escape_string(excutable_path); + + // Find directories icons and applications + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. + // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. + // $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. + // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. + // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. + std::vectortarget_candidates; + resolve_path_from_var("XDG_DATA_HOME", target_candidates); + resolve_path_from_var("XDG_DATA_DIRS", target_candidates); + + AppConfig* app_config = wxGetApp().app_config; + // suffix string to create different desktop file for alpha, beta. + + std::string version_suffix; + std::string name_suffix; + std::string version(SLIC3R_VERSION); + if (version.find("alpha") != std::string::npos) + { + version_suffix = "-alpha"; + name_suffix = " - alpha"; + } + else if (version.find("beta") != std::string::npos) + { + version_suffix = "-beta"; + name_suffix = " - beta"; + } + + // theme path to icon destination + std::string icon_theme_path; + std::string icon_theme_dirs; + + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { + icon_theme_path = "hicolor/96x96/apps/"; + icon_theme_dirs = "/hicolor/96x96/apps"; + } + + std::string target_dir_desktop; + + // desktop file + // iterate thru target_candidates to find applications folder + + std::string desktop_file_downloader = GUI::format( + "[Desktop Entry]\n" + "Name=PrusaSlicer URL Protocol%1%\n" + "Exec=\"%2%\" --single-instance %%u\n" + "Terminal=false\n" + "Type=Application\n" + "MimeType=x-scheme-handler/prusaslicer;\n" + "StartupNotify=false\n" + "NoDisplay=true\n" + , name_suffix, excutable_path); + + // desktop file for downloader as part of main app + std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (create_desktop_file(desktop_path, desktop_file_downloader)) { + // save path to desktop file + app_config->set("desktop_integration_URL_path", desktop_path); + // finish registration on mime type + std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); + BOOST_LOG_TRIVIAL(debug) << "system command: " << command; + int r = system(command.c_str()); + BOOST_LOG_TRIVIAL(debug) << "system result: " << r; + + } + + bool candidate_found = false; + for (size_t i = 0; i < target_candidates.size(); ++i) { + if (contains_path_dir(target_candidates[i], "applications")) { + target_dir_desktop = target_candidates[i]; + // Write slicer desktop file + std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (create_desktop_file(path, desktop_file_downloader)) { + app_config->set("desktop_integration_URL_path", path); + candidate_found = true; + BOOST_LOG_TRIVIAL(debug) << "PrusaSlicerURLProtocol.desktop file installation success."; + break; + } + else { + // write failed - try another path + BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicerURLProtocol.desktop file installation failed. failed path: " << target_candidates[i]; + target_dir_desktop.clear(); + } + } + } + // if all failed - try creating default home folder + if (!candidate_found) { + // create $HOME/.local/share + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); + // create desktop file + target_dir_desktop = GUI::format("%1%/.local/share", wxFileName::GetHomeDir()); + std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (contains_path_dir(target_dir_desktop, "applications")) { + if (!create_desktop_file(path, desktop_file_downloader)) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - could not create desktop file."; + return; + } + app_config->set("desktop_integration_URL_path", path); + } + else { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found."; + return; + } + } + assert(!target_dir_desktop.empty()); + if (target_dir_desktop.empty()) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found."; + show_error(nullptr, _L("Performing downloader desktop integration failed because the application directory was not found.")); + return; + } + + // finish registration on mime type + std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); + BOOST_LOG_TRIVIAL(debug) << "system command: " << command; + int r = system(command.c_str()); + BOOST_LOG_TRIVIAL(debug) << "system result: " << r; + + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); +} void DesktopIntegrationDialog::undo_downloader_registration() { const AppConfig *app_config = wxGetApp().app_config; @@ -528,7 +647,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform")); btn_szr->Add(btn_perform, 0, wxALL, 10); - btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(false); EndModal(wxID_ANY); }); + btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); }); if (can_undo){ wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo")); diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.hpp b/src/slic3r/GUI/DesktopIntegrationDialog.hpp index 08c984083..19cff1fd8 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.hpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.hpp @@ -29,10 +29,11 @@ public: // if perform_downloader: // Creates Destktop files for PrusaSlicer downloader feature // Regiters PrusaSlicer to start on prusaslicer:// URL - static void perform_desktop_integration(bool perform_downloader); + static void perform_desktop_integration(); // Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config. static void undo_desktop_intgration(); + static void perform_downloader_desktop_integration(); static void undo_downloader_registration(); private: diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index cdebbb401..520f67aed 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3085,7 +3085,7 @@ void GUI_App::show_downloader_registration_dialog() downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ if (downloader_worker->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(true); + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // __linux__ } else { app_config->set("downloader_url_registered", "0"); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 0b17043ef..a47e6065f 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -712,7 +712,7 @@ void PreferencesDialog::accept(wxEvent&) return; #ifdef __linux__ if( downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(true); + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // __linux__ } From dc0275f70d4c5d163e1906828cfc08ee46b6d081 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 16 Feb 2023 16:22:56 +0100 Subject: [PATCH 11/13] Cut bug fixing: * Fix for https://dev.prusa3d.com/browse/SPE-1489 - The text is misaligned in cut dialog. * Fix for https://dev.prusa3d.com/browse/SPE-1382 - Cut Plane does not move smoothly for multipart models. + Code cleanup - unused code is removed. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 78 ++++++++++------------------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 ++- 2 files changed, 30 insertions(+), 54 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 194f9c6f8..e87978a22 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -244,7 +244,7 @@ std::string GLGizmoCut3D::get_tooltip() const if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); - const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); + const BoundingBoxf3& tbb = m_transformed_bounding_box; if (tbb.max.z() >= 0.0) { double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")"; @@ -401,7 +401,7 @@ bool GLGizmoCut3D::is_looking_forward() const void GLGizmoCut3D::update_clipper() { - BoundingBoxf3 box = bounding_box(); + BoundingBoxf3 box = m_bounding_box; // update cut_normal Vec3d beg, end = beg = m_plane_center; @@ -549,7 +549,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v return !is_approx(old_val, value); }; - const BoundingBoxf3 bbox = bounding_box(); + const BoundingBoxf3 bbox = m_bounding_box; const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f); ImGuiWrapper::text(label); @@ -795,7 +795,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - const double mean_size = get_grabber_mean_size(bounding_box()); + const double mean_size = get_grabber_mean_size(m_bounding_box); double size; const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane; @@ -1033,7 +1033,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() else if (!cut_line_processing()){ const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m; - const BoundingBoxf3 box = bounding_box(); + const BoundingBoxf3 box = m_bounding_box; const double size = get_half_size(get_grabber_mean_size(box)); Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -1183,7 +1183,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) Vec3d rotation = Vec3d::Zero(); rotation[m_hover_id] = theta; - m_rotation_m = m_start_dragging_m * rotation_transform(rotation); + + const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); + if (m_rotation_m.rotation() != rotation_tmp.rotation()) + m_transformed_bounding_box = transformed_bounding_box(m_plane_center); + m_rotation_m = rotation_tmp; m_angle = theta; while (m_angle > two_pi) @@ -1245,9 +1249,13 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { + if (m_plane_center == center_pos) + return; + bool can_set_center_pos = force; + BoundingBoxf3 tbb; if (!can_set_center_pos) { - const BoundingBoxf3 tbb = transformed_bounding_box(center_pos); + tbb = transformed_bounding_box(center_pos); if (tbb.max.z() > -1. && tbb.min.z() < 1.) can_set_center_pos = true; else { @@ -1260,6 +1268,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false* } if (can_set_center_pos) { + m_transformed_bounding_box = tbb; m_plane_center = center_pos; m_center_offset = m_plane_center - m_bb_center; } @@ -1279,7 +1288,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const return ret; } -BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, bool revert_move /*= false*/) const +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center) const { // #ysFIXME !!! BoundingBoxf3 ret; @@ -1299,10 +1308,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, Vec3d cut_center_offset = plane_center - instance_offset; cut_center_offset[Z] -= sel_info->get_sla_shift(); - const auto move = translation_transform(-cut_center_offset); - const auto move2 = translation_transform(plane_center); - - const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move; + const auto cut_matrix = Transform3d::Identity() * m_rotation_m.inverse() * translation_transform(-cut_center_offset); const Selection& selection = m_parent.get_selection(); const Selection::IndicesList& idxs = selection.get_volume_idxs(); @@ -1335,6 +1341,8 @@ bool GLGizmoCut3D::update_bb() const BoundingBoxf3 box = bounding_box(); if (m_max_pos != box.max || m_min_pos != box.min) { + m_bounding_box = box; + invalidate_cut_plane(); m_max_pos = box.max; @@ -1388,7 +1396,7 @@ void GLGizmoCut3D::init_picking_models() } if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { - const double cp_width = 0.02 * get_grabber_mean_size(bounding_box()); + const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); m_plane.model.init_from(its); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); @@ -1631,9 +1639,8 @@ void GLGizmoCut3D::render_build_size() { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); - const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); - Vec3d tbb_sz = tbb.size(); + Vec3d tbb_sz = m_transformed_bounding_box.size(); wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; @@ -1646,7 +1653,7 @@ void GLGizmoCut3D::render_build_size() void GLGizmoCut3D::reset_cut_plane() { - set_center(bounding_box().center()); + set_center(m_bb_center); m_rotation_m = Transform3d::Identity(); m_angle_arc.reset(); update_clipper(); @@ -1746,7 +1753,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) const bool has_connectors = !connectors.empty(); - const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center; + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center == m_plane_center; m_imgui->disabled_begin(is_cut_plane_init); if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) reset_cut_plane(); @@ -1838,10 +1845,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); m_imgui->disabled_begin(has_connectors); - add_horizontal_shift(m_imgui->scaled(/*1*/.2f)); ImGuiWrapper::text(_L("Cut to") + ":"); - ImGui::SameLine(); + add_horizontal_scaled_interval(1.2f); if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) m_keep_as_parts = false; ImGui::SameLine(); @@ -1990,38 +1996,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_debug_input_window(x); } -// get volume transformation regarding to the "border". Border is related from the size of connectors -Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const -{ - bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); -#if ENABLE_WORLD_COORDINATE - const Transform3d connector_trafo = is_prizm_dowel ? - Geometry::translation_transform(-m_connector_depth_ratio * Vec3d::UnitZ()) * m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, 2 * m_connector_depth_ratio }) : - m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, m_connector_depth_ratio }); - -#else - const Transform3d connector_trafo = assemble_transform( - is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), - Transformation(m_rotation_m).get_rotation(), - Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio), - Vec3d::Ones()); -#endif // ENABLE_WORLD_COORDINATE - const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size(); - - const Vec3d bb = volume->mesh().bounding_box().size(); - - // calculate an unused border - part of the the volume, where we can't put connectors - const Vec3d border_scale(connector_bb.x() / bb.x(), connector_bb.y() / bb.y(), connector_bb.z() / bb.z()); - - const Transform3d vol_matrix = volume->get_matrix(); - const Vec3d vol_trans = vol_matrix.translation(); - // offset of the volume will be changed after scaling, so calculate the needed offset and set it to a volume_trafo - const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z()); - - // scale and translate volume to suppress to put connectors too close to the border - return translation_transform(offset) * scale_transform(Vec3d::Ones() - border_scale) * vol_matrix; -} - bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane @@ -2072,7 +2046,7 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix); // check if connector's bounding box is inside the object's bounding box - if (!bounding_box().contains(cur_tbb)) { + if (!m_bounding_box.contains(cur_tbb)) { m_info_stats.outside_bb++; return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 3b7663171..569122d4a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -43,6 +43,9 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; + BoundingBoxf3 m_bounding_box; + BoundingBoxf3 m_transformed_bounding_box; + // values from RotationGizmo double m_radius{ 0.0 }; double m_grabber_radius{ 0.0 }; @@ -193,7 +196,7 @@ public: void invalidate_cut_plane(); BoundingBoxf3 bounding_box() const; - BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const; + BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center) const; protected: bool on_init() override; @@ -263,7 +266,6 @@ private: void render_connect_mode_radio_button(CutConnectorMode mode); bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; bool render_connect_type_radio_button(CutConnectorType type); - Transform3d get_volume_transformation(const ModelVolume* volume) const; bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); void render_connectors(); From b5b548a140c2f61b93210df759c27f6ecea9abfb Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Feb 2023 09:53:22 +0100 Subject: [PATCH 12/13] automatic painting: fix error message on windows when converting from std string to wxstring - use from_u8 --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 9f0d838c3..aeba4aa81 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -521,7 +521,7 @@ void GLGizmoFdmSupports::auto_generate() { std::string err = wxGetApp().plater()->fff_print().validate(); if (!err.empty()) { - MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + err, _L("Warning"), wxOK); + MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + from_u8(err), _L("Warning"), wxOK); dlg.ShowModal(); return; } From 08135550e27468e6f2e4a5daa884c574559a6760 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 1 Feb 2023 09:35:18 +0100 Subject: [PATCH 13/13] Fix of hints arrow for gizmos and hints text --- resources/data/hints.ini | 4 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index a418c6a89..af0e476e1 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -237,14 +237,14 @@ documentation_link = https://help.prusa3d.com/article/prusaslicer-printables-com weight = 3 [hint:Cut tool] -text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation. +text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation. documentation_link = https://help.prusa3d.com/article/cut-tool_1779 hypertext_type = gizmo hypertext_gizmo_item = cut weight = 3 [hint:Measurement tool] -text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation. +text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation. documentation_link = https://help.prusa3d.com/article/measurement-tool_399451 hypertext_type = gizmo hypertext_gizmo_item = measure diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a76fbb764..299778be5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7055,7 +7055,7 @@ void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManage { if (m_timer.IsRunning()) invalidate(); - if (!gizmo || !canvas) + if (gizmo == GLGizmosManager::EType::Undefined || !canvas) return; m_timer.Start(300, false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 21a1f9ba5..d7d6f309d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -740,6 +740,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w; const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h; const float stride_y = 2.0f * m_layout.scaled_stride_y() * inv_cnv_h; + top_y -= stride_y; for (size_t idx : selectable_idxs) { if (idx == highlighted_type) {