mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-20 14:08:20 +00:00
Merge impl branch
This commit is contained in:
commit
e45d52efd1
2934 files changed
+209925
-266931
No files matched your search
@@ -625,6 +625,12 @@ void Bed3D::update_model_offset()
|
||||
shift(2) = -0.03;
|
||||
Vec3d* model_offset_ptr = const_cast<Vec3d*>(&m_model_offset);
|
||||
*model_offset_ptr = shift;
|
||||
//BBS: TODO: hack for current stl for BBL printer
|
||||
if (std::string::npos != m_model_filename.find("bbl-3dp-"))
|
||||
{
|
||||
(*model_offset_ptr)(0) -= m_bed_shape[2].x() / 2.0f;
|
||||
(*model_offset_ptr)(1) -= m_bed_shape[2].y() / 2.0f;
|
||||
}
|
||||
(*model_offset_ptr)(2) = -0.41 + GROUND_Z;
|
||||
|
||||
// update extended bounding box
|
||||
|
||||
@@ -819,7 +819,7 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
|
||||
|
||||
m_scrolled_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL);
|
||||
m_scrolled_window->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolled_window->SetScrollRate(0, FromDIP(10));
|
||||
m_scrolled_window->SetScrollRate(0, FromDIP(20));
|
||||
|
||||
wxBoxSizer *title_sizer_h= new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *title_sizer_v = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -1000,13 +1000,13 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) :
|
||||
show_pos.x = screen_size.GetLeft();
|
||||
m_scrolled_window->SetMaxSize(wxSize(screen_size.GetWidth(), popup_height));
|
||||
m_scrolled_window->SetMinSize(wxSize(screen_size.GetWidth(), popup_height));
|
||||
m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(10));
|
||||
m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(20));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scrolled_window->SetMaxSize(wxSize(popup_width, popup_height));
|
||||
m_scrolled_window->SetMinSize(wxSize(popup_width, popup_height));
|
||||
m_scrolled_window->SetScrollRate(0, FromDIP(10));
|
||||
m_scrolled_window->SetScrollRate(0, FromDIP(20));
|
||||
}
|
||||
|
||||
//Vertical Direction Processing
|
||||
@@ -2117,7 +2117,7 @@ void AmsReplaceMaterialDialog::create()
|
||||
identical_filament->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#009688"))); // Orca: accent teal (not brand green)
|
||||
|
||||
m_scrollview_groups = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
|
||||
m_scrollview_groups->SetScrollRate(5, 5);
|
||||
m_scrollview_groups->SetScrollRate(5, FromDIP(20));
|
||||
//m_scrollview_groups->SetMinSize(wxSize(400, 400));
|
||||
//m_scrollview_groups->SetMaxSize(wxSize(400, 400));
|
||||
m_scrollview_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
@@ -606,7 +606,7 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo
|
||||
wxBoxSizer *sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
|
||||
m_scrolledWindow->SetScrollRate(5, 5);
|
||||
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
|
||||
wxBoxSizer *sizer_body = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *sizer_top = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
@@ -863,17 +863,27 @@ void AuxiliaryPanel::init_tabpanel()
|
||||
m_tabpanel->SetBackgroundColour(wxColour("#FEFFFF"));
|
||||
m_tabpanel->Bind(wxEVT_BOOKCTRL_PAGE_CHANGED, [](wxBookCtrlEvent &e) { /* Event handling */ });
|
||||
|
||||
m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER);
|
||||
m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE);
|
||||
m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS);
|
||||
m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE);
|
||||
m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS);
|
||||
|
||||
m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true);
|
||||
m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false);
|
||||
m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false);
|
||||
m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false);
|
||||
m_tabpanel->AddPage(m_others_panel, _L("Others"), false);
|
||||
add_build_step([this] {
|
||||
m_designer_panel = new DesignerPanel(m_tabpanel, AuxiliaryFolderType::DESIGNER);
|
||||
m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_pictures_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::MODEL_PICTURE);
|
||||
m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_bill_of_materials_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::BILL_OF_MATERIALS);
|
||||
m_tabpanel->AddPage(m_bill_of_materials_panel, _L("Bill of Materials"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE);
|
||||
m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS);
|
||||
m_tabpanel->AddPage(m_others_panel, _L("Others"), false);
|
||||
Layout();
|
||||
});
|
||||
}
|
||||
|
||||
wxWindow *AuxiliaryPanel::create_side_tools()
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "slic3r/GUI/UpgradePanel.hpp"
|
||||
#include "slic3r/GUI/AmsWidgets.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
#define AUFILE_GREY700 wxColour(107, 107, 107)
|
||||
#define AUFILE_GREY500 wxColour(158, 158, 158)
|
||||
@@ -194,7 +195,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class AuxiliaryPanel : public wxPanel
|
||||
class AuxiliaryPanel : public wxPanel, public StagedBuild
|
||||
{
|
||||
private:
|
||||
Tabbook *m_tabpanel = {nullptr};
|
||||
|
||||
@@ -558,7 +558,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
//show bind failed info
|
||||
m_sw_bind_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(450), FromDIP(300)), wxVSCROLL);
|
||||
m_sw_bind_failed_info->SetBackgroundColour(*wxWHITE);
|
||||
m_sw_bind_failed_info->SetScrollRate(5, 5);
|
||||
m_sw_bind_failed_info->SetScrollRate(5, FromDIP(20));
|
||||
m_sw_bind_failed_info->SetMinSize(wxSize(FromDIP(450), FromDIP(90)));
|
||||
m_sw_bind_failed_info->SetMaxSize(wxSize(FromDIP(450), FromDIP(90)));
|
||||
|
||||
|
||||
@@ -3521,7 +3521,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
||||
cards->Show(m_box_mate, false, true);
|
||||
|
||||
m_form->FitInside();
|
||||
m_form->SetScrollRate(0, 10); // vertical only, like Prepare's sidebar: never scroll labels out
|
||||
m_form->SetScrollRate(0, FromDIP(20)); // vertical only, like Prepare's sidebar: never scroll labels out
|
||||
m_form->SetMinSize(wxSize(264, -1));
|
||||
|
||||
// Right column: a small view toolbar over the live 3D viewport that mirrors
|
||||
@@ -4862,13 +4862,13 @@ void DesignPanel::on_import_mesh()
|
||||
try {
|
||||
shape = GeometryEngine::mesh_to_brep(mesh.its, MESH_IMPORT_TOLERANCE,
|
||||
MESH_IMPORT_MERGE_ANGLE_DEG, stats);
|
||||
} catch (const std::exception& e) {
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(e.what()));
|
||||
return;
|
||||
} catch (const Standard_Failure& e) { // OCCT throws outside std::exception
|
||||
} catch (const Standard_Failure& e) { // on OCCT >= 8 Standard_Failure derives from std::exception — must precede that handler
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(
|
||||
e.GetMessageString() ? e.GetMessageString() : "OCCT error"));
|
||||
return;
|
||||
} catch (const std::exception& e) {
|
||||
fail(_L("Mesh conversion failed: ") + wxString::FromUTF8(e.what()));
|
||||
return;
|
||||
}
|
||||
if (shape.IsNull()) { fail(_L("Mesh conversion produced no geometry")); return; }
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "libslic3r/CAD/CadDocument.hpp"
|
||||
#include "slic3r/GUI/CAD/DesignInteraction.hpp" // CadLevel: what one Esc press means
|
||||
#include "slic3r/GUI/Lazy.hpp"
|
||||
|
||||
class ComboBox; // Orca dropdown (Widgets/ComboBox.hpp) — replaces wxChoice everywhere here
|
||||
class StaticBox; // Orca rounded card frame (Widgets/StaticBox.hpp)
|
||||
@@ -46,7 +47,7 @@ class DesignCanvas;
|
||||
// Design (CAD) tab: a sketch-first, Onshape-style form-driven CAD panel.
|
||||
// Sketch and Extrude are independent tools: the user creates a Sketch first,
|
||||
// then selects it and Extrudes to produce a solid.
|
||||
class DesignPanel : public wxPanel
|
||||
class DesignPanel : public wxPanel, public LazyInstance<DesignPanel>
|
||||
{
|
||||
public:
|
||||
explicit DesignPanel(wxWindow* parent);
|
||||
|
||||
@@ -1989,11 +1989,10 @@ json action_set_feature_expr(DesignPanel* panel, const json& params)
|
||||
// Dispatch one parsed request ON THE MAIN THREAD. Returns a JSON-RPC reply string.
|
||||
std::string handle_on_main(const std::string& method, const json& params, const json& id)
|
||||
{
|
||||
MainFrame* mf = wxGetApp().mainframe;
|
||||
// The panel is built on first use, and in a headless session nobody clicks the tab that
|
||||
// would build it -- so build it here rather than refusing. Safe: this runs on the main
|
||||
// thread (see the CallAfter that dispatches us).
|
||||
DesignPanel* panel = mf ? mf->ensure_design_panel() : nullptr;
|
||||
DesignPanel* panel = DesignPanel::ensure();
|
||||
if (!panel)
|
||||
return rpc_error(id, -32001, "Design panel not ready");
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ HistoryWindow::HistoryWindow(wxWindow* parent, const std::vector<PACalibResult>&
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto scroll_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
|
||||
scroll_window->SetScrollRate(5, 5);
|
||||
scroll_window->SetScrollRate(5, FromDIP(20));
|
||||
scroll_window->SetBackgroundColour(*wxWHITE);
|
||||
scroll_window->SetMinSize(HISTORY_WINDOW_SIZE);
|
||||
scroll_window->SetSize(HISTORY_WINDOW_SIZE);
|
||||
|
||||
@@ -207,7 +207,7 @@ SelectMObjectPopup::SelectMObjectPopup(wxWindow* parent)
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL);
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE);
|
||||
m_scrolledWindow->SetScrollRate(0, 5);
|
||||
m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y);
|
||||
auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL);
|
||||
m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow);
|
||||
m_scrolledWindow->Layout();
|
||||
@@ -491,14 +491,15 @@ void CalibrationPanel::init_tabpanel() {
|
||||
selected);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)CALI_MODE_COUNT; i++)
|
||||
add_build_steps_of(*m_cali_panels[i]);
|
||||
|
||||
// ORCA use standard paddings and keep arrow icon for consistent look between sidebars
|
||||
//for (int i = 0; i < (int)CALI_MODE_COUNT; i++)
|
||||
// m_tabpanel->SetPageImage(i, "");
|
||||
|
||||
//auto padding_size = m_tabpanel->GetBtnsListCtrl()->GetPaddingSize(0);
|
||||
//m_tabpanel->GetBtnsListCtrl()->SetPaddingSize({ FromDIP(15), padding_size.y });
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void CalibrationPanel::init_timer()
|
||||
@@ -534,6 +535,8 @@ void CalibrationPanel::update_print_error_info(int code, std::string msg, std::s
|
||||
}
|
||||
|
||||
void CalibrationPanel::update_all() {
|
||||
// Every wizard's pages exist once the last build step has run.
|
||||
if (!built()) return;
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
@@ -597,7 +600,7 @@ void CalibrationPanel::update_all() {
|
||||
|
||||
void CalibrationPanel::show_status(int status)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
if (!built()) return;
|
||||
if (last_status == status)return;
|
||||
last_status = status;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "CalibrationWizard.hpp"
|
||||
#include "Tabbook.hpp"
|
||||
#include "Lazy.hpp"
|
||||
//#include "Widgets/SideTools.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -88,7 +89,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class CalibrationPanel : public wxPanel
|
||||
class CalibrationPanel : public wxPanel, public StagedBuild, public LazyInstance<CalibrationPanel>
|
||||
{
|
||||
public:
|
||||
CalibrationPanel(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
|
||||
@@ -109,7 +110,6 @@ protected:
|
||||
|
||||
|
||||
int last_status;
|
||||
bool m_initialized { false };
|
||||
std::string last_conn_type = "undedefined";
|
||||
MachineObject* obj{ nullptr };
|
||||
MachineObject* last_obj { nullptr };
|
||||
|
||||
@@ -81,7 +81,7 @@ CalibrationWizard::CalibrationWizard(wxWindow* parent, CalibMode mode, wxWindowI
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL);
|
||||
m_scrolledWindow->SetScrollRate(5, 5);
|
||||
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* padding_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
@@ -130,6 +130,15 @@ CalibrationWizard::~CalibrationWizard()
|
||||
;
|
||||
}
|
||||
|
||||
void CalibrationWizard::add_page_step(CalibrationWizardPageStep*& step, std::function<CalibrationWizardPage*()> make)
|
||||
{
|
||||
add_build_step([this, &step, make = std::move(make)] {
|
||||
step = new CalibrationWizardPageStep(make());
|
||||
m_all_pages_sizer->Add(step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
step->page->Hide();
|
||||
});
|
||||
}
|
||||
|
||||
void CalibrationWizard::on_cali_job_finished(wxCommandEvent& event)
|
||||
{
|
||||
this->on_cali_job_finished(event.GetString());
|
||||
@@ -520,33 +529,28 @@ void PressureAdvanceWizard::on_cali_job_finished(wxString evt_data)
|
||||
|
||||
void PressureAdvanceWizard::create_pages()
|
||||
{
|
||||
start_step = new CalibrationWizardPageStep(new CalibrationPAStartPage(m_scrolledWindow));
|
||||
preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false));
|
||||
cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode));
|
||||
save_step = new CalibrationWizardPageStep(new CalibrationPASavePage(m_scrolledWindow));
|
||||
add_page_step(start_step, [this] { return new CalibrationPAStartPage(m_scrolledWindow); });
|
||||
add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); });
|
||||
add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); });
|
||||
add_page_step(save_step, [this] { return new CalibrationPASavePage(m_scrolledWindow); });
|
||||
|
||||
m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
add_build_step([this] {
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_step);
|
||||
m_page_steps.push_back(save_step);
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() -1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i+1]);
|
||||
}
|
||||
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_step);
|
||||
m_page_steps.push_back(save_step);
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() -1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i+1]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Hide();
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &PressureAdvanceWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
});
|
||||
}
|
||||
|
||||
void PressureAdvanceWizard::on_cali_action(wxCommandEvent& evt)
|
||||
@@ -1053,59 +1057,46 @@ FlowRateWizard::FlowRateWizard(wxWindow* parent, wxWindowID id, const wxPoint& p
|
||||
|
||||
void FlowRateWizard::create_pages()
|
||||
{
|
||||
start_step = new CalibrationWizardPageStep(new CalibrationFlowRateStartPage(m_scrolledWindow));
|
||||
preset_step = new CalibrationWizardPageStep(new CalibrationPresetPage(m_scrolledWindow, m_mode, false));
|
||||
add_page_step(start_step, [this] { return new CalibrationFlowRateStartPage(m_scrolledWindow); });
|
||||
add_page_step(preset_step, [this] { return new CalibrationPresetPage(m_scrolledWindow, m_mode, false); });
|
||||
|
||||
// manual
|
||||
cali_coarse_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI));
|
||||
coarse_save_step = new CalibrationWizardPageStep(new CalibrationFlowCoarseSavePage(m_scrolledWindow));
|
||||
cali_fine_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI));
|
||||
fine_save_step = new CalibrationWizardPageStep(new CalibrationFlowFineSavePage(m_scrolledWindow));
|
||||
add_page_step(cali_coarse_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_CALI); });
|
||||
add_page_step(coarse_save_step, [this] { return new CalibrationFlowCoarseSavePage(m_scrolledWindow); });
|
||||
add_page_step(cali_fine_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode, CaliPageType::CALI_PAGE_FINE_CALI); });
|
||||
add_page_step(fine_save_step, [this] { return new CalibrationFlowFineSavePage(m_scrolledWindow); });
|
||||
|
||||
// auto
|
||||
cali_step = new CalibrationWizardPageStep(new CalibrationCaliPage(m_scrolledWindow, m_mode));
|
||||
save_step = new CalibrationWizardPageStep(new CalibrationFlowX1SavePage(m_scrolledWindow));
|
||||
add_page_step(cali_step, [this] { return new CalibrationCaliPage(m_scrolledWindow, m_mode); });
|
||||
add_page_step(save_step, [this] { return new CalibrationFlowX1SavePage(m_scrolledWindow); });
|
||||
|
||||
m_all_pages_sizer->Add(start_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(preset_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_coarse_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(coarse_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(cali_fine_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(fine_save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
add_build_step([this] {
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_coarse_step);
|
||||
m_page_steps.push_back(coarse_save_step);
|
||||
m_page_steps.push_back(cali_fine_step);
|
||||
m_page_steps.push_back(fine_save_step);
|
||||
|
||||
m_all_pages_sizer->Add(cali_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
m_all_pages_sizer->Add(save_step->page, 1, wxEXPAND | wxALL, FromDIP(25));
|
||||
//m_page_steps.push_back(cali_step);
|
||||
//m_page_steps.push_back(save_step);
|
||||
|
||||
m_page_steps.push_back(start_step);
|
||||
m_page_steps.push_back(preset_step);
|
||||
m_page_steps.push_back(cali_coarse_step);
|
||||
m_page_steps.push_back(coarse_save_step);
|
||||
m_page_steps.push_back(cali_fine_step);
|
||||
m_page_steps.push_back(fine_save_step);
|
||||
for (int i = 0; i < m_page_steps.size() - 1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i + 1]);
|
||||
}
|
||||
|
||||
//m_page_steps.push_back(cali_step);
|
||||
//m_page_steps.push_back(save_step);
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_page_steps.size() - 1; i++) {
|
||||
m_page_steps[i]->chain(m_page_steps[i + 1]);
|
||||
}
|
||||
cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
|
||||
// hide all pages
|
||||
cali_step->page->Hide();
|
||||
save_step->page->Hide();
|
||||
for (int i = 0; i < m_page_steps.size(); i++) {
|
||||
m_page_steps[i]->page->Hide();
|
||||
m_page_steps[i]->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
}
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
|
||||
|
||||
cali_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
save_step->page->Bind(EVT_CALI_ACTION, &FlowRateWizard::on_cali_action, this);
|
||||
|
||||
if (!m_page_steps.empty())
|
||||
show_step(m_page_steps.front());
|
||||
|
||||
set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL);
|
||||
set_cali_method(CalibrationMethod::CALI_METHOD_MANUAL);
|
||||
});
|
||||
}
|
||||
|
||||
void FlowRateWizard::on_cali_action(wxCommandEvent& evt)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "CalibrationWizardPresetPage.hpp"
|
||||
#include "CalibrationWizardCaliPage.hpp"
|
||||
#include "CalibrationWizardSavePage.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
@@ -36,7 +37,7 @@ struct ConfigIndexValue
|
||||
int index{0};
|
||||
};
|
||||
|
||||
class CalibrationWizard : public wxPanel {
|
||||
class CalibrationWizard : public wxPanel, public StagedBuild {
|
||||
public:
|
||||
CalibrationWizard(wxWindow* parent, CalibMode mode,
|
||||
wxWindowID id = wxID_ANY,
|
||||
@@ -79,6 +80,9 @@ public:
|
||||
protected:
|
||||
void on_cali_go_home();
|
||||
|
||||
// Queues a page as a build step, created hidden and added to the pages sizer.
|
||||
void add_page_step(CalibrationWizardPageStep*& step, std::function<CalibrationWizardPage*()> make);
|
||||
|
||||
protected:
|
||||
/* wx widgets*/
|
||||
wxScrolledWindow* m_scrolledWindow;
|
||||
|
||||
@@ -192,8 +192,10 @@ void CalibrationCaliPage::update(MachineObject* obj)
|
||||
set_cali_img();
|
||||
}
|
||||
|
||||
// A calibration can run before the Device tab is ever opened, and only its status
|
||||
// panel shows a print error.
|
||||
if (obj->print_error > 0) {
|
||||
StatusPanel* status_panel = Slic3r::GUI::wxGetApp().mainframe->m_monitor->get_status_panel();
|
||||
StatusPanel* status_panel = MonitorPanel::ensure()->get_status_panel();
|
||||
status_panel->obj = obj;
|
||||
status_panel->update_error_message();
|
||||
}
|
||||
|
||||
@@ -834,7 +834,7 @@ void CaliPageSendingPanel::create(wxWindow* parent)
|
||||
|
||||
m_sw_print_failed_info = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL);
|
||||
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
|
||||
m_sw_print_failed_info->SetScrollRate(0, 5);
|
||||
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
|
||||
m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125)));
|
||||
m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125)));
|
||||
|
||||
|
||||
@@ -1046,6 +1046,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
|
||||
// Orca: both tower generators skip sparse layers, so this is not a wipe tower 2 exclusive.
|
||||
toggle_line("wipe_tower_no_sparse_layers", have_prime_tower);
|
||||
// Dropping the sparse layers outright leaves nothing to combine, so the two are exclusive.
|
||||
toggle_line("wipe_tower_sparse_layers_combination", have_prime_tower && !config->opt_bool("wipe_tower_no_sparse_layers"));
|
||||
|
||||
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
|
||||
bool have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower;
|
||||
|
||||
@@ -2710,7 +2710,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
// We can now enable scrolling on hscroll
|
||||
p->hscroll->SetScrollRate(30, 30);
|
||||
p->hscroll->SetScrollRate(30, FromDIP(20));
|
||||
|
||||
on_window_geometry(this, [this]() {
|
||||
p->init_dialog_size();
|
||||
|
||||
@@ -699,7 +699,7 @@ CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent)
|
||||
m_scrolled_preset_panel = new wxScrolledWindow(this, wxID_ANY);
|
||||
m_scrolled_preset_panel->SetMaxSize(wxSize(-1, FromDIP(350)));
|
||||
m_scrolled_preset_panel->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolled_preset_panel->SetScrollRate(5, 5);
|
||||
m_scrolled_preset_panel->SetScrollRate(5, FromDIP(20));
|
||||
m_scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_scrolled_sizer->Add(create_item(FilamentOptionType::PRESET_FOR_PRINTER), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5));
|
||||
m_scrolled_sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
|
||||
@@ -1593,7 +1593,7 @@ CreatePrinterPresetDialog::CreatePrinterPresetDialog(wxWindow *parent)
|
||||
|
||||
m_page1 = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_page1->SetBackgroundColour(*wxWHITE);
|
||||
m_page1->SetScrollRate(5, 5);
|
||||
m_page1->SetScrollRate(5, FromDIP(20));
|
||||
m_page2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_page2->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
create_printer_page1(m_page1);
|
||||
@@ -2652,7 +2652,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_presets_template_item(wxWindow *pa
|
||||
wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_scrolled_preset_window = new wxScrolledWindow(parent);
|
||||
m_scrolled_preset_window->SetScrollRate(5, 5);
|
||||
m_scrolled_preset_window->SetScrollRate(5, FromDIP(20));
|
||||
m_scrolled_preset_window->SetBackgroundColour(*wxWHITE);
|
||||
//m_scrolled_preset_window->SetMinSize(wxSize(FromDIP(1500), FromDIP(-1)));
|
||||
m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(1500), FromDIP(-1)));
|
||||
@@ -4297,7 +4297,7 @@ wxBoxSizer *ExportConfigsDialog::create_select_printer(wxWindow *parent)
|
||||
optionSizer->SetMinSize(OPTION_SIZE);
|
||||
horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL, FromDIP(10));
|
||||
m_scrolled_preset_window = new wxScrolledWindow(parent);
|
||||
m_scrolled_preset_window->SetScrollRate(5, 5);
|
||||
m_scrolled_preset_window->SetScrollRate(5, FromDIP(20));
|
||||
m_scrolled_preset_window->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(660), FromDIP(400)));
|
||||
m_scrolled_preset_window->SetSize(wxSize(FromDIP(660), FromDIP(400)));
|
||||
@@ -4736,7 +4736,7 @@ wxBoxSizer *EditFilamentPresetDialog::create_preset_tree_sizer()
|
||||
{
|
||||
wxBoxSizer *filament_preset_tree_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_preset_tree_window = new wxScrolledWindow(this);
|
||||
m_preset_tree_window->SetScrollRate(5, 5);
|
||||
m_preset_tree_window->SetScrollRate(5, FromDIP(20));
|
||||
m_preset_tree_window->SetBackgroundColour(PRINTER_LIST_COLOUR);
|
||||
m_preset_tree_window->SetMinSize(wxSize(-1, FromDIP(400)));
|
||||
m_preset_tree_window->SetMaxSize(wxSize(-1, FromDIP(300)));
|
||||
|
||||
@@ -39,7 +39,7 @@ DeviceErrorDialog::DeviceErrorDialog(MachineObject* obj, wxWindow* parent, wxWin
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
|
||||
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_scroll_area->SetScrollRate(0, 5);
|
||||
m_scroll_area->SetScrollRate(0, FromDIP(20));
|
||||
m_scroll_area->SetBackgroundColour(*wxWHITE);
|
||||
m_scroll_area->SetMinSize(wxSize(FromDIP(320), FromDIP(250)));
|
||||
|
||||
@@ -538,7 +538,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::CHECK_ASSISTANT: {
|
||||
wxGetApp().mainframe->m_monitor->jump_to_HMS(); // go to assistant page
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_HMS(); // go to assistant page
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::FILAMENT_EXTRUDED: {
|
||||
@@ -568,7 +569,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
case DeviceErrorDialog::JUMP_TO_LIVEVIEW: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_LiveView();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_LiveView();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::NO_REMINDER_NEXT_TIME: {
|
||||
@@ -618,7 +620,8 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Rack();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ABORT: {
|
||||
|
||||
@@ -934,7 +934,8 @@ void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt)
|
||||
dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true);
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Upgrade();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,8 @@ void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->jump_to_Upgrade();
|
||||
|
||||
wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId());
|
||||
evt.SetEventObject(this);
|
||||
|
||||
@@ -1443,6 +1443,7 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
||||
libvgcode::EGCodeExtrusionRole::SupportTransition, libvgcode::EGCodeExtrusionRole::Mixed
|
||||
});
|
||||
m_paths_bounding_box = BoundingBoxf3(libvgcode::convert(bbox[0]).cast<double>(), libvgcode::convert(bbox[1]).cast<double>());
|
||||
m_max_bounding_box = m_paths_bounding_box;
|
||||
|
||||
if (wxGetApp().is_editor())
|
||||
m_contained_in_bed = wxGetApp().plater()->build_volume().all_paths_inside(gcode_result, m_paths_bounding_box);
|
||||
|
||||
+235
-60
@@ -20,6 +20,7 @@
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "BackgroundSlicingProcess.hpp"
|
||||
#include "CameraUtils.hpp"
|
||||
#include "GLShader.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "Tab.hpp"
|
||||
@@ -117,6 +118,36 @@ void GLCanvas3D::load_render_colors()
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static void pan_camera(Camera& camera, const Vec2d& screen_delta, const Vec3d& anchor)
|
||||
{
|
||||
// Orca: Derive world-units-per-pixel from the projection which produced the visible frame.
|
||||
// Perspective additionally scales with the eye-space depth of the point being dragged.
|
||||
const auto& viewport = camera.get_viewport();
|
||||
const auto& projection = camera.get_projection_matrix().matrix();
|
||||
const double depth_scale = camera.get_type() == Camera::EType::Perspective ?
|
||||
(anchor - camera.get_position()).dot(camera.get_dir_forward()) : 1.0;
|
||||
const double projection_x = projection(0, 0) * viewport[2];
|
||||
const double projection_y = projection(1, 1) * viewport[3];
|
||||
|
||||
// Orca: X/Y projection coefficients already include zoom. Using them directly avoids a
|
||||
// project/unproject round-trip through window depth, whose precision depends on the scene frustum.
|
||||
if (viewport[2] > 0 && viewport[3] > 0 && anchor.allFinite() && depth_scale > EPSILON &&
|
||||
std::abs(projection_x) > EPSILON && std::abs(projection_y) > EPSILON) {
|
||||
const Vec3d displacement = 2.0 * depth_scale *
|
||||
(screen_delta.y() / projection_y * camera.get_dir_up() -
|
||||
screen_delta.x() / projection_x * camera.get_dir_right());
|
||||
if (displacement.allFinite()) {
|
||||
camera.translate(displacement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: Preserve the former target-plane behavior if the projection or anchor is invalid.
|
||||
// Screen Y grows downward, and the camera moves opposite to the drag.
|
||||
camera.translate(camera.get_inv_zoom() *
|
||||
(screen_delta.y() * camera.get_dir_up() - screen_delta.x() * camera.get_dir_right()));
|
||||
}
|
||||
|
||||
#ifdef __WXGTK3__
|
||||
// wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support.
|
||||
RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {}
|
||||
@@ -1057,8 +1088,6 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SWITCH_TO_OBJECT, SimpleEvent);
|
||||
@@ -2206,15 +2235,14 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
_render_background();
|
||||
|
||||
//BBS add partplater rendering logic
|
||||
bool only_current = false, only_body = false, no_partplate = false;
|
||||
bool only_current = false, only_body = false;
|
||||
const bool show_bed = is_bed_visible();
|
||||
bool show_grid = true;
|
||||
GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
|
||||
if (!m_main_toolbar.is_enabled()) {
|
||||
//only_body = true;
|
||||
only_current = true;
|
||||
}
|
||||
else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmSegmentation) || (gizmo_type == GLGizmosManager::FuzzySkin))
|
||||
no_partplate = true;
|
||||
else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward())
|
||||
show_grid = false;
|
||||
if (m_axes_at_bed_center)
|
||||
@@ -2228,11 +2256,11 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
if (!no_partplate && m_show_bed)
|
||||
if (show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), m_show_world_axes);
|
||||
if (!no_partplate && m_show_bed) //BBS: add outline logic
|
||||
if (show_bed) //BBS: add outline logic
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
if (m_axes_at_bed_center && m_show_bed && !no_partplate)
|
||||
if (m_axes_at_bed_center && show_bed)
|
||||
// Design tab: replace the plate's corner-origin grid with the origin-centred CAD grid.
|
||||
_render_cad_grid(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
|
||||
@@ -2324,6 +2352,13 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data,
|
||||
render_thumbnail(thumbnail_data, w, h, thumbnail_params, model_objects, m_volumes, camera_type, camera_view_angle_type, for_picking, ban_light);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_set_shown_canvas_current()
|
||||
{
|
||||
// Thumbnails also render outside render(), where another library's GL context (e.g. WebKitGTK's) can be current.
|
||||
// Inside render(), the shown canvas is the one already bound.
|
||||
return wxGetApp().plater()->get_current_canvas3D()->_set_current();
|
||||
}
|
||||
|
||||
void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data,
|
||||
unsigned int w,
|
||||
unsigned int h,
|
||||
@@ -2335,6 +2370,9 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_data,
|
||||
bool for_picking,
|
||||
bool ban_light)
|
||||
{
|
||||
if (!_set_shown_canvas_current())
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = nullptr;
|
||||
if (for_picking)
|
||||
shader = wxGetApp().get_shader("flat");
|
||||
@@ -2373,6 +2411,9 @@ void GLCanvas3D::render_thumbnail(ThumbnailData & thumbnail_d
|
||||
bool for_picking,
|
||||
bool ban_light)
|
||||
{
|
||||
if (!_set_shown_canvas_current())
|
||||
return;
|
||||
|
||||
GLShaderProgram *shader = wxGetApp().get_shader("thumbnail");
|
||||
switch (OpenGLManager::get_framebuffers_type()) {
|
||||
case OpenGLManager::EFramebufferType::Arb: {
|
||||
@@ -3281,6 +3322,9 @@ void GLCanvas3D::unbind_event_handlers()
|
||||
m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||
m_canvas->Unbind(wxEVT_GESTURE_ZOOM, &GLCanvas3D::on_gesture, this);
|
||||
m_canvas->Unbind(wxEVT_GESTURE_ROTATE, &GLCanvas3D::on_gesture, this);
|
||||
#if __WXOSX__
|
||||
initGestures(m_canvas->GetHandle(), nullptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4029,27 +4073,38 @@ void GLCanvas3D::on_gesture(wxGestureEvent &evt)
|
||||
|
||||
auto & camera = wxGetApp().plater()->get_camera();
|
||||
if (evt.GetEventType() == wxEVT_GESTURE_PAN) {
|
||||
auto p = evt.GetPosition();
|
||||
// Orca: Gesture coordinates must use framebuffer pixels, and one stable world-space
|
||||
// anchor must be retained for the complete gesture to prevent perspective drift.
|
||||
const auto p = evt.GetPosition();
|
||||
auto d = static_cast<wxPanGestureEvent&>(evt).GetDelta();
|
||||
float z = 0;
|
||||
const Vec3d &p2 = _mouse_to_3d({p.x, p.y}, &z);
|
||||
const Vec3d &p1 = _mouse_to_3d({p.x - d.x, p.y - d.y}, &z);
|
||||
camera.set_target(camera.get_target() + p1 - p2);
|
||||
Vec2d screen_position(p.x, p.y);
|
||||
Vec2d screen_delta(d.x, d.y);
|
||||
apply_retina_scale(screen_position);
|
||||
apply_retina_scale(screen_delta);
|
||||
if (evt.IsGestureStart() || !m_gesture_pan_anchor.has_value())
|
||||
m_gesture_pan_anchor = get_camera_pan_anchor(camera, ECameraNavigationType::Gesture,
|
||||
screen_position - screen_delta);
|
||||
pan_camera(camera, screen_delta, *m_gesture_pan_anchor);
|
||||
if (evt.IsGestureEnd())
|
||||
m_gesture_pan_anchor.reset();
|
||||
} else if (evt.GetEventType() == wxEVT_GESTURE_ZOOM) {
|
||||
static float zoom_start = 1;
|
||||
if (evt.IsGestureStart())
|
||||
zoom_start = camera.get_zoom();
|
||||
camera.set_zoom(zoom_start * static_cast<wxZoomGestureEvent&>(evt).GetZoomFactor());
|
||||
} else if (evt.GetEventType() == wxEVT_GESTURE_ROTATE) {
|
||||
PartPlate* plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
// Orca: Rotation starts a different navigation operation, so a previous pan anchor
|
||||
// must not be reused; rotation and pan fallbacks share the same navigation pivot.
|
||||
m_gesture_pan_anchor.reset();
|
||||
bool rotate_limit = current_printer_technology() != ptSLA;
|
||||
static double last_rotate = 0;
|
||||
if (evt.IsGestureStart())
|
||||
last_rotate = 0;
|
||||
auto rotate = static_cast<wxRotateGestureEvent&>(evt).GetRotationAngle() - last_rotate;
|
||||
last_rotate += rotate;
|
||||
if (plate)
|
||||
camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, plate->get_bounding_box().center());
|
||||
const std::optional<Vec3d> rotate_target = get_camera_orbit_target(ECameraNavigationType::Gesture);
|
||||
if (rotate_target.has_value())
|
||||
camera.rotate_on_sphere_with_target(-rotate, 0, rotate_limit, *rotate_target);
|
||||
else
|
||||
camera.rotate_on_sphere(-rotate, 0, rotate_limit);
|
||||
camera.auto_type(Camera::EType::Perspective);
|
||||
@@ -4339,6 +4394,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_SWITCH_TO_GLOBAL));
|
||||
}
|
||||
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown()) {
|
||||
// Orca: Retain the click position even if the first motion event crosses a surface edge.
|
||||
m_mouse.set_start_position_2D_as_invalid();
|
||||
m_mouse.drag.start_position_2D = pos;
|
||||
|
||||
//BBS: add orient deactivate logic
|
||||
if (!m_gizmos.on_mouse(evt)) {
|
||||
if (_deactivate_arrange_menu() || _deactivate_orient_menu())
|
||||
@@ -4532,6 +4591,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
// do not process the dragging if the left mouse was set down in another canvas
|
||||
else if (is_camera_rotate(evt, button_mappings)) {
|
||||
// Orca: Rotation and panning use different drag coordinates and cached anchors.
|
||||
// Clear the pan state before processing rotation or switching buttons mid-drag.
|
||||
m_mouse.set_start_position_2D_as_invalid();
|
||||
|
||||
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
|
||||
m_canvas->CaptureMouse();
|
||||
@@ -4549,12 +4611,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports ||
|
||||
m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmSegmentation ||
|
||||
m_gizmos.get_current_type() == GLGizmosManager::FuzzySkin) {
|
||||
Vec3d rotate_target = Vec3d::Zero();
|
||||
if (!m_selection.is_empty())
|
||||
rotate_target = m_selection.get_bounding_box().center();
|
||||
// Orca: Reuse the centralized pivot policy for scene-oriented tools.
|
||||
const std::optional<Vec3d> rotate_target = get_camera_orbit_target(ECameraNavigationType::Mouse);
|
||||
if (rotate_target.has_value())
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, *rotate_target);
|
||||
else
|
||||
rotate_target = volumes_bounding_box().center();
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, rotate_target);
|
||||
camera.rotate_on_sphere(rot.x(), rot.y(), false);
|
||||
}
|
||||
else {
|
||||
if (wxGetApp().app_config->get_bool("use_free_camera"))
|
||||
@@ -4578,28 +4640,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, m_rotation_center);
|
||||
} else {
|
||||
Vec3d rotate_target = Vec3d::Zero();
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview) {
|
||||
PartPlate *plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
if (plate)
|
||||
rotate_target = plate->get_bounding_box().center();
|
||||
}
|
||||
else {
|
||||
if (!m_selection.is_empty())
|
||||
rotate_target = m_selection.get_bounding_box().center();
|
||||
else {
|
||||
// Rotate around the center of objects on current plate
|
||||
auto bbox = volumes_bounding_box(true);
|
||||
if (!bbox.defined) {
|
||||
// Rotate around current plate center if current plate is empty
|
||||
bbox = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_bounding_box();
|
||||
}
|
||||
rotate_target = bbox.center();
|
||||
}
|
||||
}
|
||||
|
||||
if (!rotate_target.isZero())
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target);
|
||||
// Orca: Keep regular mouse orbit and perspective-pan fallback centered
|
||||
// on the same selection, active-plate, or scene reference.
|
||||
const std::optional<Vec3d> rotate_target = get_camera_orbit_target(ECameraNavigationType::Mouse);
|
||||
if (rotate_target.has_value())
|
||||
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, *rotate_target);
|
||||
else
|
||||
camera.rotate_on_sphere(rot.x(), rot.y(), rotate_limit);
|
||||
}
|
||||
@@ -4615,16 +4660,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
|
||||
}
|
||||
else if (is_camera_pan(evt, button_mappings)) {
|
||||
// Orca: Pan uses screen coordinates and must not inherit the rotation start point.
|
||||
m_mouse.set_start_position_3D_as_invalid();
|
||||
|
||||
if (!has_mouse_capture()) // ORCA keep tracking mouse position while drag active and cursor not in window bounds
|
||||
m_canvas->CaptureMouse();
|
||||
|
||||
// if dragging with right button or if button functions swapped and dragging with left button over blank area then pan
|
||||
if (m_mouse.is_start_position_2D_defined()) {
|
||||
// get point in model space at Z = 0
|
||||
float z = 0.0f;
|
||||
const Vec3d& cur_pos = _mouse_to_3d(pos, &z);
|
||||
Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z);
|
||||
Camera& camera = wxGetApp().plater()->get_camera();
|
||||
if (this->m_canvas_type != ECanvasType::CanvasAssembleView) {
|
||||
// Orca: Use a constrained camera when navigating the 3D scene with a regular mouse, if the free camera is not selected
|
||||
@@ -4636,7 +4679,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
camera.recover_from_free_camera();
|
||||
}
|
||||
|
||||
camera.set_target(camera.get_target() + orig - cur_pos);
|
||||
// Orca: Cache the surface under the initial click and apply every incremental
|
||||
// cursor delta at that depth, so perspective zoom and camera angle stay exact.
|
||||
const Vec2d screen_delta =
|
||||
pos.cast<double>() - m_mouse.drag.start_position_2D.cast<double>();
|
||||
if (!m_mouse.drag.camera_pan_anchor.has_value())
|
||||
m_mouse.drag.camera_pan_anchor = get_camera_pan_anchor(camera, ECameraNavigationType::Mouse,
|
||||
m_mouse.drag.start_position_2D.cast<double>());
|
||||
pan_camera(camera, screen_delta, *m_mouse.drag.camera_pan_anchor);
|
||||
m_dirty = true;
|
||||
m_mouse.ignore_right_up = true; // will be reset on button up event even if not right button is pressed
|
||||
}
|
||||
@@ -7231,6 +7281,16 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
||||
m_last_w = w;
|
||||
m_last_h = h;
|
||||
|
||||
set_imgui_scaling();
|
||||
|
||||
this->request_extra_frame();
|
||||
|
||||
// ensures that this canvas is current
|
||||
_set_current();
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_imgui_scaling()
|
||||
{
|
||||
float font_size = wxGetApp().em_unit();
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -7243,15 +7303,10 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
||||
#endif
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
imgui->set_scaling(font_size, 1.0f, m_retina_helper->get_scale_factor());
|
||||
wxGetApp().imgui()->set_scaling(font_size, 1.0f, m_retina_helper->get_scale_factor());
|
||||
#else
|
||||
imgui->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f);
|
||||
wxGetApp().imgui()->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f);
|
||||
#endif
|
||||
|
||||
this->request_extra_frame();
|
||||
|
||||
// ensures that this canvas is current
|
||||
_set_current();
|
||||
}
|
||||
|
||||
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model, bool include_plates) const
|
||||
@@ -7329,11 +7384,8 @@ void GLCanvas3D::_picking_pass()
|
||||
m_hover_volume_idxs.clear();
|
||||
m_hover_plate_idxs.clear();
|
||||
|
||||
// Orca: ignore clipping plane if not applying
|
||||
GLGizmoBase *current_gizmo = m_gizmos.get_current();
|
||||
const ClippingPlane clipping_plane = ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() :
|
||||
ClippingPlane::ClipsNothing())
|
||||
.inverted_normal();
|
||||
// Orca: Picking and camera navigation must interpret the active gizmo clipping plane identically.
|
||||
const ClippingPlane clipping_plane = get_raycaster_clipping_plane();
|
||||
const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(m_mouse.position, wxGetApp().plater()->get_camera(), &clipping_plane);
|
||||
if (hit.is_valid()) {
|
||||
switch (hit.type)
|
||||
@@ -10627,6 +10679,129 @@ Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos)
|
||||
return mouse_ray(mouse_pos).intersect_plane(0.0);
|
||||
}
|
||||
|
||||
ClippingPlane GLCanvas3D::get_raycaster_clipping_plane() const
|
||||
{
|
||||
// Orca: Ignore the gizmo clipping plane when the active tool does not apply it, and
|
||||
// invert the result into the convention expected by SceneRaycaster.
|
||||
GLGizmoBase* current_gizmo = m_gizmos.get_current();
|
||||
return ((!current_gizmo || current_gizmo->apply_clipping_plane()) ? m_gizmos.get_clipping_plane() :
|
||||
ClippingPlane::ClipsNothing())
|
||||
.inverted_normal();
|
||||
}
|
||||
|
||||
std::optional<Vec3d> GLCanvas3D::get_camera_orbit_target(ECameraNavigationType navigation_type) const
|
||||
{
|
||||
// Orca: Centralize the pre-existing pivot rules so orbiting and pan fallback cannot
|
||||
// choose different reference depths for the same canvas and active tool.
|
||||
PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
if (navigation_type == ECameraNavigationType::Gesture)
|
||||
return current_plate == nullptr ? std::nullopt :
|
||||
std::make_optional(current_plate->get_bounding_box().center());
|
||||
|
||||
const GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
|
||||
const bool use_scene_target = m_canvas_type == ECanvasType::CanvasAssembleView ||
|
||||
gizmo_type == GLGizmosManager::FdmSupports || gizmo_type == GLGizmosManager::Seam ||
|
||||
gizmo_type == GLGizmosManager::MmSegmentation || gizmo_type == GLGizmosManager::FuzzySkin;
|
||||
if (use_scene_target) {
|
||||
if (!m_selection.is_empty())
|
||||
return m_selection.get_bounding_box().center();
|
||||
|
||||
// Orca: Preserve the world-origin fallback used by orbit in an empty scene.
|
||||
return volumes_bounding_box().center();
|
||||
}
|
||||
|
||||
// Orca: Free-camera rotation uses Camera::m_target rather than a plate or selection pivot.
|
||||
if (wxGetApp().app_config->get_bool("use_free_camera"))
|
||||
return std::nullopt;
|
||||
|
||||
Vec3d target = Vec3d::Zero();
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview) {
|
||||
if (current_plate != nullptr)
|
||||
target = current_plate->get_bounding_box().center();
|
||||
} else if (!m_selection.is_empty()) {
|
||||
target = m_selection.get_bounding_box().center();
|
||||
} else {
|
||||
// Orca: Match regular mouse orbit: objects on the active plate, then the plate itself.
|
||||
BoundingBoxf3 bbox = volumes_bounding_box(true);
|
||||
if (!bbox.defined && current_plate != nullptr)
|
||||
bbox = current_plate->get_bounding_box();
|
||||
if (bbox.defined)
|
||||
target = bbox.center();
|
||||
}
|
||||
|
||||
// Orca: Preserve the existing zero sentinel used by regular mouse orbit.
|
||||
return target.isZero() ? std::nullopt : std::make_optional(target);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::is_bed_visible() const
|
||||
{
|
||||
if (m_canvas_type == ECanvasType::CanvasPreview)
|
||||
return m_render_preview;
|
||||
if (m_canvas_type != ECanvasType::CanvasView3D || !m_show_bed)
|
||||
return false;
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
return true;
|
||||
|
||||
const auto type = m_gizmos.get_current_type();
|
||||
return type != GLGizmosManager::FdmSupports && type != GLGizmosManager::Seam &&
|
||||
type != GLGizmosManager::MmSegmentation && type != GLGizmosManager::FuzzySkin;
|
||||
}
|
||||
|
||||
Vec3d GLCanvas3D::get_camera_pan_anchor(Camera& camera, ECameraNavigationType navigation_type,
|
||||
const Vec2d& screen_position) const
|
||||
{
|
||||
// Orthographic panning has the same scale at every depth, so no raycast is needed.
|
||||
if (camera.get_type() != Camera::EType::Perspective)
|
||||
return camera.get_target();
|
||||
|
||||
// Orca: Reject non-finite anchors and points behind the camera before their depth is
|
||||
// allowed to scale a perspective pan.
|
||||
const Vec3d camera_position = camera.get_position();
|
||||
const Vec3d camera_forward = camera.get_dir_forward();
|
||||
const auto is_valid_anchor = [&camera_position, &camera_forward](const Vec3d& anchor) {
|
||||
return anchor.allFinite() && (anchor - camera_position).dot(camera_forward) > EPSILON;
|
||||
};
|
||||
|
||||
// Orca: Prefer the nearest visible bed or volume surface and exclude gizmos and
|
||||
// selected-volume picking priority from navigation depth selection.
|
||||
const ClippingPlane clipping_plane = get_raycaster_clipping_plane();
|
||||
const bool bed_visible = is_bed_visible();
|
||||
const SceneRaycaster::HitResult hit = m_scene_raycaster.hit(screen_position, camera, &clipping_plane,
|
||||
bed_visible ? SceneRaycaster::EHitMode::SceneOnly : SceneRaycaster::EHitMode::VolumesOnly);
|
||||
if (hit.is_valid()) {
|
||||
const Vec3d hit_position = hit.position.cast<double>();
|
||||
if (is_valid_anchor(hit_position))
|
||||
return hit_position;
|
||||
}
|
||||
|
||||
// Orca: When the cursor is just outside the visible plate, use the point under it on the active
|
||||
// plate plane. Using the plate center here would give it a different perspective depth.
|
||||
PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate();
|
||||
// Orca: An almost edge-on perspective makes intersection depth extremely sensitive to
|
||||
// the cursor's vertical position. Use the stable orbit depth around horizontal views.
|
||||
static constexpr double min_plate_plane_forward_z = 0.05;
|
||||
if (bed_visible && std::abs(camera_forward.z()) >= min_plate_plane_forward_z &&
|
||||
current_plate != nullptr && current_plate->get_bounding_box().defined) {
|
||||
Vec3d ray_origin;
|
||||
Vec3d ray_direction;
|
||||
CameraUtils::ray_from_screen_pos(camera, screen_position, ray_origin, ray_direction);
|
||||
const double z_direction = ray_direction.z();
|
||||
if (ray_origin.allFinite() && ray_direction.allFinite() && std::abs(z_direction) > EPSILON) {
|
||||
const double plate_z = current_plate->get_bounding_box().center().z();
|
||||
const double distance = (plate_z - ray_origin.z()) / z_direction;
|
||||
const Vec3d plate_position = ray_origin + distance * ray_direction;
|
||||
const double eye_depth = (plate_position - camera_position).dot(camera_forward);
|
||||
if (distance >= 0.0 && is_valid_anchor(plate_position) &&
|
||||
eye_depth >= camera.get_near_z() && eye_depth <= camera.get_far_z())
|
||||
return plate_position;
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: Near-horizontal rays and points outside the scene depth use the orbit reference point.
|
||||
const std::optional<Vec3d> orbit_target = get_camera_orbit_target(navigation_type);
|
||||
return orbit_target.has_value() && is_valid_anchor(*orbit_target) ? *orbit_target : camera.get_target();
|
||||
}
|
||||
|
||||
// While it looks like we can call
|
||||
// this->reload_scene(true, true)
|
||||
// the two functions are quite different:
|
||||
|
||||
@@ -185,8 +185,6 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_SLIDERS, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_JUMP_TO, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SWITCH_TO_OBJECT, SimpleEvent);
|
||||
@@ -338,6 +336,8 @@ class GLCanvas3D
|
||||
int move_volume_idx{ -1 };
|
||||
bool move_requires_threshold{ false };
|
||||
Point move_start_threshold_position_2D{ Invalid_2D_Point };
|
||||
// Orca: Keep the world-space point selected at the start of a mouse pan.
|
||||
std::optional<Vec3d> camera_pan_anchor;
|
||||
};
|
||||
|
||||
bool dragging{ false };
|
||||
@@ -346,7 +346,12 @@ class GLCanvas3D
|
||||
Drag drag;
|
||||
bool ignore_right_up;
|
||||
|
||||
void set_start_position_2D_as_invalid() { drag.start_position_2D = Drag::Invalid_2D_Point; }
|
||||
// Orca: The screen-space start and world-space anchor describe the same pan session.
|
||||
// Invalidating one must invalidate the other so a new drag cannot reuse stale depth.
|
||||
void set_start_position_2D_as_invalid() {
|
||||
drag.start_position_2D = Drag::Invalid_2D_Point;
|
||||
drag.camera_pan_anchor.reset();
|
||||
}
|
||||
void set_start_position_3D_as_invalid() { drag.start_position_3D = Drag::Invalid_3D_Point; }
|
||||
void set_move_start_threshold_position_2D_as_invalid() { drag.move_start_threshold_position_2D = Drag::Invalid_2D_Point; }
|
||||
|
||||
@@ -549,6 +554,8 @@ private:
|
||||
bool m_fps_overlay_tick{ false };
|
||||
LayersEditing m_layers_editing;
|
||||
Mouse m_mouse;
|
||||
// Orca: Gesture pans have their own lifecycle and stable world-space anchor.
|
||||
std::optional<Vec3d> m_gesture_pan_anchor;
|
||||
GLGizmosManager m_gizmos;
|
||||
//BBS: GUI refactor: GLToolbar
|
||||
mutable GLToolbar m_main_toolbar;
|
||||
@@ -1292,6 +1299,8 @@ public:
|
||||
Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr);
|
||||
|
||||
bool make_current_for_postinit();
|
||||
// Sizes ImGui's fonts and style for this canvas; the fonts are rebuilt when the size changes.
|
||||
void set_imgui_scaling();
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
@@ -1311,6 +1320,7 @@ private:
|
||||
bool _init_collapse_toolbar();
|
||||
|
||||
bool _set_current();
|
||||
bool _set_shown_canvas_current();
|
||||
void _resize(unsigned int w, unsigned int h);
|
||||
|
||||
//BBS: add part plate related logic
|
||||
@@ -1406,6 +1416,20 @@ private:
|
||||
// Convert the screen space coordinate to world coordinate on the bed.
|
||||
Vec3d _mouse_to_bed_3d(const Point& mouse_pos);
|
||||
|
||||
// Orca: Navigation type selects the legacy pivot policy used when no visible surface is hit.
|
||||
enum class ECameraNavigationType : unsigned char
|
||||
{
|
||||
Mouse,
|
||||
Gesture
|
||||
};
|
||||
|
||||
// Orca: These helpers keep clipping, orbit pivots, and perspective-pan depth selection consistent.
|
||||
ClippingPlane get_raycaster_clipping_plane() const;
|
||||
bool is_bed_visible() const;
|
||||
std::optional<Vec3d> get_camera_orbit_target(ECameraNavigationType navigation_type) const;
|
||||
Vec3d get_camera_pan_anchor(Camera& camera, ECameraNavigationType navigation_type,
|
||||
const Vec2d& screen_position) const;
|
||||
|
||||
void _start_timer() { m_timer.Start(100, wxTIMER_CONTINUOUS); }
|
||||
void _stop_timer() { m_timer.Stop(); }
|
||||
|
||||
|
||||
@@ -456,8 +456,9 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
||||
data.reserve_vertices(3 * its.indices.size());
|
||||
data.reserve_indices(3 * its.indices.size());
|
||||
|
||||
// Read user preference: smooth normals enabled
|
||||
const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
|
||||
// CLI thumbnail generation can initialize models without a wx application.
|
||||
const bool smooth_normals_enabled = wxApp::GetInstance() != nullptr && wxGetApp().app_config != nullptr &&
|
||||
wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS);
|
||||
|
||||
if (smooth_normals_enabled) {
|
||||
// Use per-corner smooth normals (via IGL)
|
||||
|
||||
+67
-33
@@ -854,13 +854,18 @@ void GUI_App::post_init()
|
||||
slow_bootup = true;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", slow bootup, won't render gl here.";
|
||||
}
|
||||
if (!switch_to_3d) {
|
||||
// Starting on Home, the GL resources load at idle so Home paints first and Prepare is never
|
||||
// shown.
|
||||
const bool gl_at_idle = !starts_on_prepare() && is_editor();
|
||||
if (!switch_to_3d && gl_at_idle) {
|
||||
plater_->select_view_3D("3D");
|
||||
} else if (!switch_to_3d) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", begin load_gl_resources";
|
||||
#ifndef __linux__
|
||||
mainframe->Freeze();
|
||||
#endif
|
||||
plater_->canvas3D()->enable_render(false);
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
mainframe->select_prepare_for_gl_init();
|
||||
plater_->select_view_3D("3D");
|
||||
//BBS init the opengl resource here
|
||||
if (!plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen() ||
|
||||
@@ -897,9 +902,7 @@ void GUI_App::post_init()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test";
|
||||
}
|
||||
}
|
||||
if (is_editor())
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
if (app_config->get("default_page") == "1")
|
||||
if (starts_on_prepare())
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
#ifndef __linux__
|
||||
mainframe->Thaw();
|
||||
@@ -909,6 +912,7 @@ void GUI_App::post_init()
|
||||
|
||||
plater_->trigger_restore_project(1);
|
||||
//#endif
|
||||
mainframe->prebuild_pages_when_idle();
|
||||
|
||||
//BBS: remove GCodeViewer as seperate APP logic
|
||||
/*if (this->init_params->start_as_gcodeviewer) {
|
||||
@@ -1923,9 +1927,9 @@ bool GUI_App::hot_reload_network_plugin()
|
||||
m_device_manager->add_user_subscribe();
|
||||
}
|
||||
|
||||
if (mainframe && mainframe->m_monitor) {
|
||||
mainframe->m_monitor->update_network_version_footer();
|
||||
mainframe->m_monitor->set_default();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->update_network_version_footer();
|
||||
monitor->set_default();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": reset monitor panel";
|
||||
}
|
||||
|
||||
@@ -3405,14 +3409,20 @@ bool GUI_App::on_init_inner()
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "create the main window";
|
||||
mainframe = new MainFrame();
|
||||
// hide settings tabs after first Layout
|
||||
// The first render can happen as soon as the frame is shown, before the queued
|
||||
// new_project() sets the same view.
|
||||
plater_->get_camera().select_view("topfront");
|
||||
plater_->get_camera().requires_zoom_to_bed = true;
|
||||
if (is_editor()) {
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
if (starts_on_prepare()) {
|
||||
mainframe->select_tab(TAB_ID_PREPARE);
|
||||
} else {
|
||||
mainframe->select_tab(TAB_ID_HOME);
|
||||
}
|
||||
}
|
||||
|
||||
sidebar().obj_list()->init();
|
||||
//sidebar().aux_list()->init_auxiliary();
|
||||
mainframe->m_project->init_auxiliary();
|
||||
|
||||
// update_mode(); // !!! do that later
|
||||
SetTopWindow(mainframe);
|
||||
@@ -4122,13 +4132,13 @@ void GUI_App::select_machine(const std::string& agent_id)
|
||||
|
||||
// Use MonitorPanel::select_machine() to trigger full selection flow
|
||||
// This reuses existing logic for machine switching (UI updates, callbacks, etc.)
|
||||
if (mainframe && mainframe->m_monitor) {
|
||||
mainframe->m_monitor->select_machine(dev_id);
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->select_machine(dev_id);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": triggered select_machine for dev_id=" << dev_id;
|
||||
} else {
|
||||
// Fallback if MonitorPanel not available
|
||||
m_device_manager->set_selected_machine(dev_id);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fallback set_selected_machine dev_id=" << dev_id;
|
||||
} else if (m_device_manager->set_selected_machine(dev_id)) {
|
||||
// The Device tab's own state is set when the tab is built.
|
||||
MonitorPanel::on_machine_selected(m_device_manager->get_selected_machine());
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": set_selected_machine dev_id=" << dev_id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4652,6 +4662,7 @@ void GUI_App::recreate_GUI(const wxString &msg_name)
|
||||
|
||||
//BBS: trigger restore project logic here, and skip confirm
|
||||
plater_->trigger_restore_project(1);
|
||||
mainframe->prebuild_pages_when_idle();
|
||||
|
||||
// #ys_FIXME_delete_after_testing Do we still need this ?
|
||||
// CallAfter([]() {
|
||||
@@ -4993,7 +5004,8 @@ void GUI_App::get_login_info(const std::string& provider/* = ORCA_CLOUD_PROVIDER
|
||||
wxString strJS = wxString::Format("window.postMessage(%s)", from_u8(logout_cmd));
|
||||
GUI::wxGetApp().run_script(strJS);
|
||||
}
|
||||
mainframe->m_webview->SetLoginPanelVisibility(true);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SetLoginPanelVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5127,9 +5139,9 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
"homepage_bambu_login_or_register",
|
||||
};
|
||||
if (app_config->get_stealth_mode() && stealth_blocked_info_commands.count(command_str)) {
|
||||
CallAfter([this] {
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
CallAfter([] {
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
});
|
||||
return "";
|
||||
}
|
||||
@@ -5143,8 +5155,8 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
app_config->set_bool("stealth_mode", false);
|
||||
app_config->save();
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
// Continue with login
|
||||
if (command_str == "homepage_login_or_register")
|
||||
this->request_login(true);
|
||||
@@ -5225,8 +5237,8 @@ std::string GUI_App::handle_web_request(std::string cmd)
|
||||
}
|
||||
else if (command_str.compare("get_recent_projects") == 0) {
|
||||
if (mainframe) {
|
||||
if (mainframe->m_webview) {
|
||||
mainframe->m_webview->SendRecentList(INT_MAX);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendRecentList(INT_MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7759,8 +7771,8 @@ void GUI_App::on_stealth_mode_enter()
|
||||
BOOST_LOG_TRIVIAL(info) << "logout: on_stealth_mode_enter";
|
||||
request_user_logout(ORCA_CLOUD_PROVIDER);
|
||||
request_user_logout(BBL_CLOUD_PROVIDER);
|
||||
if (mainframe && mainframe->m_webview) {
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendCloudProvidersInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8170,6 +8182,26 @@ ConfigOptionMode GUI_App::get_saved_mode()
|
||||
return saved_mode_from_string(app_config->get("user_mode"));
|
||||
}
|
||||
|
||||
bool GUI_App::starts_on_prepare() const
|
||||
{
|
||||
return app_config->get("default_page") == "1";
|
||||
}
|
||||
|
||||
int GUI_App::input_idle_ms() const
|
||||
{
|
||||
return int(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_last_input).count());
|
||||
}
|
||||
|
||||
// Every wxCommandEvent claims the user-input category, so only real mouse and key events count,
|
||||
// plus main window resizes, since a border drag produces no mouse events.
|
||||
int GUI_App::FilterEvent(wxEvent& event)
|
||||
{
|
||||
if ((!event.IsCommandEvent() && (event.GetEventCategory() & wxEVT_CATEGORY_USER_INPUT)) ||
|
||||
(event.GetEventType() == wxEVT_SIZE && event.GetEventObject() == mainframe))
|
||||
m_last_input = std::chrono::steady_clock::now();
|
||||
return Event_Skip;
|
||||
}
|
||||
|
||||
ConfigOptionMode GUI_App::get_mode()
|
||||
{
|
||||
return app_config->get_bool("developer_mode") ? comDevelop : get_saved_mode();
|
||||
@@ -8221,9 +8253,10 @@ void GUI_App::update_mode()
|
||||
mainframe->m_param_panel->update_mode();
|
||||
if (mainframe->m_param_dialog)
|
||||
mainframe->m_param_dialog->panel()->update_mode();
|
||||
if (mainframe->m_printer_view)
|
||||
mainframe->m_printer_view->update_mode();
|
||||
mainframe->m_webview->update_mode();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->update_mode();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->update_mode();
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
if (!wxGetApp().tabs_as_menu())
|
||||
@@ -8241,9 +8274,10 @@ void GUI_App::update_mode()
|
||||
}
|
||||
|
||||
void GUI_App::update_internal_development() {
|
||||
mainframe->m_webview->update_mode();
|
||||
if (mainframe->m_printer_view)
|
||||
mainframe->m_printer_view->update_mode();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->update_mode();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->update_mode();
|
||||
}
|
||||
|
||||
void GUI_App::show_ip_address_enter_dialog(wxString title)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_GUI_App_hpp_
|
||||
#define slic3r_GUI_App_hpp_
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -245,6 +246,7 @@ private:
|
||||
bool m_app_conf_exists{ false };
|
||||
EAppMode m_app_mode{ EAppMode::Editor };
|
||||
bool m_is_recreating_gui{ false };
|
||||
std::chrono::steady_clock::time_point m_last_input{ std::chrono::steady_clock::now() };
|
||||
#ifdef __linux__
|
||||
bool m_opengl_initialized{ false };
|
||||
#endif
|
||||
@@ -385,6 +387,11 @@ public:
|
||||
bool is_editor() const { return m_app_mode == EAppMode::Editor; }
|
||||
bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; }
|
||||
bool is_recreating_gui() const { return m_is_recreating_gui; }
|
||||
// Milliseconds since the last mouse or keyboard event the app processed, or main window resize.
|
||||
int input_idle_ms() const;
|
||||
int FilterEvent(wxEvent& event) override;
|
||||
// The Preferences "Default page" choice, stored as its index: 0 Home, 1 Prepare.
|
||||
bool starts_on_prepare() const;
|
||||
std::string logo_name() const { return is_editor() ? "OrcaSlicer" : "OrcaSlicer-gcodeviewer"; }
|
||||
|
||||
bool is_closing() const { return m_is_closing.load(std::memory_order_acquire); }
|
||||
|
||||
@@ -2764,12 +2764,12 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi
|
||||
//m_object_grid->AssignTable(m_object_grid_table);
|
||||
|
||||
m_side_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(440),FromDIP(480)), wxVSCROLL);
|
||||
m_side_window->SetScrollRate( 0, 5 );
|
||||
m_side_window->SetScrollRate(0, FromDIP(20));
|
||||
m_page_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
//m_page_top_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_side_window->SetBackgroundColour(wxColour(0xff, 0xff, 0xff));
|
||||
m_side_window->SetSizer(m_page_sizer);
|
||||
m_side_window->SetScrollbars(1, 20, 1, 2);
|
||||
m_side_window->SetScrollbars(1, FromDIP(20), 1, 2);
|
||||
//m_side_window->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
|
||||
|
||||
//m_side_window->EnableScrolling(false, true);
|
||||
|
||||
@@ -247,7 +247,9 @@ int get_dpi_for_window(const wxWindow *window)
|
||||
|
||||
const HDC hdc = GetDC(hwnd);
|
||||
if (hdc == NULL) { return DPI_DEFAULT; }
|
||||
return GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
const int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
ReleaseDC(hwnd, hdc); // GetDC's handle must be released, unlike GetWindowDC's
|
||||
return dpi;
|
||||
}
|
||||
#elif defined __linux__
|
||||
// TODO
|
||||
|
||||
@@ -122,7 +122,8 @@ HMSNotifyItem::HMSNotifyItem(const std::string& dev_id, wxWindow *parent, DevHMS
|
||||
m_hms_content->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& e) {
|
||||
wxCommandEvent evt(EVT_ALREADY_READ_HMS);
|
||||
evt.SetString(long_error_code);
|
||||
wxPostEvent(wxGetApp().mainframe->m_monitor, evt);
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
wxPostEvent(monitor, evt);
|
||||
|
||||
if (!m_url.empty()) wxLaunchDefaultBrowser(m_url);
|
||||
});
|
||||
@@ -170,7 +171,7 @@ HMSPanel::HMSPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wx
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolledWindow->SetScrollRate(5, 5);
|
||||
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
|
||||
|
||||
m_top_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "IdleScheduler.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/evtloop.h>
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
namespace {
|
||||
|
||||
// The timer's period while waiting for the user to go quiet.
|
||||
constexpr int tick_ms = 250;
|
||||
// Input-free time before a slice may start, about a double-click interval, so a user
|
||||
// mid-gesture is left alone.
|
||||
constexpr int quiet_ms = 500;
|
||||
// Budget of one slice. The next slice is a timer message, so paint, timers and input
|
||||
// queued meanwhile are handled first; a click waits at most a slice plus the unit that
|
||||
// overran it.
|
||||
constexpr int slice_ms = 40;
|
||||
// Delay before the next slice; on GTK a due timer runs ahead of repaints and posted events,
|
||||
// and wxOSX rejects a 0 ms timer.
|
||||
constexpr int next_slice_ms = 5;
|
||||
|
||||
// True when unhandled keyboard, button, touch or pen input is queued; only Windows can ask.
|
||||
bool input_pending()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Windows synthesizes a mouse move whenever a window appears under the cursor, so those
|
||||
// do not count.
|
||||
return (GetQueueStatus(QS_INPUT & ~QS_MOUSEMOVE) >> 16) != 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// True inside a wxYield(), where a slice would build pages in the middle of the code that yielded.
|
||||
bool yielding()
|
||||
{
|
||||
const wxEventLoopBase* loop = wxEventLoopBase::GetActive();
|
||||
return loop != nullptr && loop->IsYielding();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IdleScheduler::IdleScheduler(std::function<int()> input_idle_ms) : m_input_idle_ms(std::move(input_idle_ms))
|
||||
{
|
||||
m_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) { tick(); });
|
||||
}
|
||||
|
||||
void IdleScheduler::start()
|
||||
{
|
||||
if (!m_timer.IsRunning())
|
||||
m_timer.Start(tick_ms);
|
||||
}
|
||||
|
||||
void IdleScheduler::stop()
|
||||
{
|
||||
m_timer.Stop();
|
||||
}
|
||||
|
||||
void IdleScheduler::tick()
|
||||
{
|
||||
// A unit that pumps the event loop lets the timer fire inside its own slice.
|
||||
if (m_in_slice)
|
||||
return;
|
||||
if (!m_queue.pending()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
if (m_input_idle_ms() < quiet_ms || input_pending() || yielding()) {
|
||||
start();
|
||||
return;
|
||||
}
|
||||
const auto now_ms = [] {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
};
|
||||
const char* const fn = __FUNCTION__;
|
||||
m_in_slice = true;
|
||||
PrebuildQueue::Slice slice;
|
||||
{
|
||||
ScopeGuard in_slice([this] { m_in_slice = false; });
|
||||
slice = m_queue.run_slice(slice_ms, now_ms, input_pending, [fn](const std::string& name, long long ms) {
|
||||
BOOST_LOG_TRIVIAL(debug) << fn << ": " << name << ": unit took " << ms << " ms";
|
||||
});
|
||||
}
|
||||
if (slice.completed)
|
||||
BOOST_LOG_TRIVIAL(info) << fn << ": task complete: " << slice.name << ", " << slice.units << " unit(s) in " << slice.ms << " ms this slice";
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(debug) << fn << ": " << slice.name << ": " << slice.units << " unit(s) in " << slice.ms << " ms, yielding";
|
||||
if (!slice.remaining)
|
||||
stop();
|
||||
else if (input_pending())
|
||||
m_timer.Start(tick_ms);
|
||||
else
|
||||
m_timer.StartOnce(next_slice_ms);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/timer.h>
|
||||
|
||||
#include "PrebuildQueue.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Runs the queue's units while the user is idle. Once the user has been idle long enough,
|
||||
// slices run one per timer message until the work is done or input arrives, so the event
|
||||
// loop handles what it has between slices. Main thread only.
|
||||
class IdleScheduler
|
||||
{
|
||||
public:
|
||||
// input_idle_ms reports how long ago the user last touched the mouse or keyboard.
|
||||
explicit IdleScheduler(std::function<int()> input_idle_ms);
|
||||
|
||||
void add(LazyBase& task) { m_queue.add(task); }
|
||||
void clear() { m_queue.clear(); }
|
||||
// The task names in queue order, for a log line.
|
||||
std::string names() const { return m_queue.names(); }
|
||||
|
||||
// Arms the timer; it stops itself once no task is pending, so call again when a task
|
||||
// appears.
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void tick();
|
||||
|
||||
PrebuildQueue m_queue;
|
||||
wxTimer m_timer;
|
||||
std::function<int()> m_input_idle_ms;
|
||||
bool m_in_slice{ false };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -159,8 +159,12 @@ void KBShortcutsDialog::fill_pages()
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
page(_L("Global"), _L("Available anywhere in the window, even while typing in a text field."), ShortcutContext::Global, {
|
||||
fixed(Section::Application, { alt, "1-9, 0" }, L("Run a speed dial favorite while the dial is open")),
|
||||
fixed(Section::SpeedDial, { alt, "1-9, 0" }, L("Run favorite 1 to 10")),
|
||||
fixed(Section::SpeedDial, { ctrl, "B" }, L("Pin or unpin the selected action")),
|
||||
// wx cycles notebook pages on Ctrl+Tab, which is Cmd+Tab on macOS and never arrives there.
|
||||
#ifndef __APPLE__
|
||||
fixed(Section::Application, { ctrl, key(L_CONTEXT("Tab", "Keyboard Shortcut")) }, L("Switch to the next main tab")),
|
||||
#endif
|
||||
});
|
||||
|
||||
page(_L("Prepare"), _L("Available while the 3D view on the Prepare tab has focus."), ShortcutContext::Plater, {
|
||||
@@ -207,7 +211,7 @@ wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const Page& page)
|
||||
wxGetApp().UpdateDarkUI(scrollable_panel);
|
||||
const wxColour page_colour = StateColor::darkModeColorFor(*wxWHITE);
|
||||
scrollable_panel->SetBackgroundColour(page_colour);
|
||||
scrollable_panel->SetScrollRate(0, 20);
|
||||
scrollable_panel->SetScrollRate(0, FromDIP(20));
|
||||
const int page_width = FromDIP(PAGE_WIDTH);
|
||||
scrollable_panel->SetInitialSize(wxSize(page_width, FromDIP(450)));
|
||||
|
||||
@@ -454,7 +458,10 @@ ShortcutCaptureDialog::ShortcutCaptureDialog(wxWindow* parent, Shortcut shortcut
|
||||
capture_sizer->Add(m_chord_label, 0, wxALIGN_CENTER);
|
||||
capture_sizer->AddStretchSpacer();
|
||||
capture->SetSizer(capture_sizer);
|
||||
capture->Bind(wxEVT_KEY_DOWN, &ShortcutCaptureDialog::on_key, this);
|
||||
capture->Layout(); // the box is created at its final size, so nothing resizes it into laying the sizer out
|
||||
// The hook runs before the window procedure, so Windows does not open its window menu
|
||||
// over the dialog on Alt+Space.
|
||||
Bind(wxEVT_CHAR_HOOK, &ShortcutCaptureDialog::on_key, this);
|
||||
capture->Bind(wxEVT_CHAR, &ShortcutCaptureDialog::on_char, this);
|
||||
capture->Bind(wxEVT_LEFT_DOWN, [capture](wxMouseEvent&) { capture->SetFocus(); });
|
||||
sizer->Add(capture, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
|
||||
@@ -531,10 +538,12 @@ void ShortcutCaptureDialog::record(const KeyChord& chord)
|
||||
m_ok->Enable(false);
|
||||
};
|
||||
const bool global = (shortcut_info(m_shortcut).contexts & context_bit(ShortcutContext::Global)) != 0;
|
||||
if (global && !chord.is_menu_accelerator()) {
|
||||
if (chord.is_system_shortcut()) {
|
||||
reject(_L("The system uses this shortcut, so it cannot be assigned."));
|
||||
} else if (global && !chord.is_menu_accelerator()) {
|
||||
reject(m_rejection);
|
||||
} else if (const std::optional<Shortcut> owner = wxGetApp().shortcuts().step_owner(m_shortcut, chord); owner.has_value()) {
|
||||
reject(wxString::Format(_L("Already used as a step of %s."), _(shortcut_info(*owner).name)));
|
||||
reject(wxString::Format(_L("Shift and Ctrl with this key belong to %s and cannot be assigned."), _(shortcut_info(*owner).name)));
|
||||
} else {
|
||||
m_conflicts = wxGetApp().shortcuts().conflicts(m_shortcut, chord);
|
||||
m_status->SetForegroundColour(m_status_colour);
|
||||
|
||||
@@ -182,6 +182,16 @@ bool KeyChord::is_menu_accelerator() const
|
||||
return valid() && ((modifiers & (wxMOD_CONTROL | wxMOD_ALT | wxMOD_RAW_CONTROL)) != 0 || (!is_printable(key) && key != WXK_SPACE));
|
||||
}
|
||||
|
||||
// Only a chord the desktop acts on while still delivering it to the app belongs here.
|
||||
bool KeyChord::is_system_shortcut() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return modifiers == wxMOD_ALT && (key == WXK_F4 || key == WXK_SPACE);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string KeyChord::to_string() const
|
||||
{
|
||||
if (!valid())
|
||||
|
||||
@@ -34,6 +34,9 @@ struct KeyChord
|
||||
// True when Ctrl or Alt is held or the key is non-printable, the chords a menu can own without
|
||||
// swallowing typing in text fields.
|
||||
bool is_menu_accelerator() const;
|
||||
// True for a chord the desktop acts on although the app receives it, so a binding would
|
||||
// take it from the system.
|
||||
bool is_system_shortcut() const;
|
||||
|
||||
// Platform-neutral text ("Ctrl+Shift+S") for persistence and wx accelerator strings.
|
||||
std::string to_string() const;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/app.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
LazyBase::OnDemandBuild::OnDemandBuild(const LazyBase& lazy) : m_lazy(lazy), m_started(std::chrono::steady_clock::now())
|
||||
{
|
||||
// The unit tests have no app.
|
||||
if (wxTheApp != nullptr)
|
||||
m_busy = std::make_unique<wxBusyCursor>();
|
||||
}
|
||||
|
||||
LazyBase::OnDemandBuild::~OnDemandBuild()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Lazy::ensure: built " << m_lazy.name() << " on demand, " << m_units << " unit(s) in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_started).count() << " ms";
|
||||
}
|
||||
|
||||
void LazyBase::log_null_factory(const std::string& name)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Lazy::build_step: the factory for " << name << " returned null";
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
class wxBusyCursor;
|
||||
|
||||
// Deferred construction of one object. Lazy<T> holds the factory and builds the object on
|
||||
// demand or, unit by unit, from an IdleScheduler; LazyBase is what the scheduler sees of
|
||||
// it; LazyInstance<Self> gives a type with one such object app-wide static access to it.
|
||||
// docs/HLSD/deferred-page-construction.md covers the design.
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
template <class Self> class LazyInstance;
|
||||
|
||||
// A prebuild task: what PrebuildQueue sees of a Lazy<T>. A type with its own unit sequence
|
||||
// implements it directly.
|
||||
class LazyBase
|
||||
{
|
||||
public:
|
||||
virtual ~LazyBase() = default;
|
||||
|
||||
// Labels the object in logs.
|
||||
virtual const std::string& name() const = 0;
|
||||
virtual bool built() const = 0;
|
||||
// Whether the idle prebuild has work here.
|
||||
virtual bool pending() const { return !built(); }
|
||||
// Runs one unit and returns true while more remain.
|
||||
virtual bool build_step() = 0;
|
||||
// Position in the idle queue: lower builds first, negative is never prebuilt.
|
||||
virtual int prebuild_order() const = 0;
|
||||
|
||||
protected:
|
||||
// Busy cursor around a build the user is waiting on, and a log line after it.
|
||||
class OnDemandBuild
|
||||
{
|
||||
public:
|
||||
explicit OnDemandBuild(const LazyBase& lazy);
|
||||
~OnDemandBuild();
|
||||
void unit() { ++m_units; }
|
||||
|
||||
private:
|
||||
const LazyBase& m_lazy;
|
||||
std::unique_ptr<wxBusyCursor> m_busy;
|
||||
std::chrono::steady_clock::time_point m_started;
|
||||
int m_units{ 0 };
|
||||
};
|
||||
|
||||
static void log_null_factory(const std::string& name);
|
||||
};
|
||||
|
||||
// Holds an object a factory makes on the first build_step() or ensure(), followed by one
|
||||
// StagedBuild step per unit if the type has them, and keeps callbacks until the object is
|
||||
// complete. The object's parent owns it, not the holder. A LazyInstance type is registered
|
||||
// for its statics; any other type is reached only through the holder.
|
||||
template <class T>
|
||||
class Lazy : public LazyBase
|
||||
{
|
||||
public:
|
||||
using Factory = std::function<T*()>;
|
||||
|
||||
Lazy(std::string name, int order, Factory make) : m_name(std::move(name)), m_order(order), m_make(std::move(make))
|
||||
{
|
||||
if constexpr (registered())
|
||||
LazyInstance<T>::s_lazy = this;
|
||||
}
|
||||
~Lazy() override
|
||||
{
|
||||
if constexpr (registered())
|
||||
if (LazyInstance<T>::s_lazy == this)
|
||||
LazyInstance<T>::s_lazy = nullptr;
|
||||
}
|
||||
Lazy(const Lazy&) = delete;
|
||||
Lazy& operator=(const Lazy&) = delete;
|
||||
|
||||
// Null until completely built.
|
||||
T* get() const { return built() ? m_object : nullptr; }
|
||||
|
||||
// Builds whatever is left now and returns the object, null if the factory returned null
|
||||
// or a nested call finds the object mid-build.
|
||||
T* ensure()
|
||||
{
|
||||
if (!built() && !m_building) {
|
||||
OnDemandBuild build(*this);
|
||||
do
|
||||
build.unit();
|
||||
while (build_step());
|
||||
}
|
||||
return get();
|
||||
}
|
||||
|
||||
// Runs fn on the object now if it is built, otherwise once it is.
|
||||
void when_built(std::function<void(T&)> fn)
|
||||
{
|
||||
if (built())
|
||||
fn(*m_object);
|
||||
else
|
||||
m_deferred.push_back(std::move(fn));
|
||||
}
|
||||
|
||||
const std::string& name() const override { return m_name; }
|
||||
// Read by a worker thread through the statics; the last write in build_step() releases it.
|
||||
bool built() const override { return m_complete.load(std::memory_order_acquire); }
|
||||
bool pending() const override { return !built() && !m_failed; }
|
||||
int prebuild_order() const override { return m_order; }
|
||||
|
||||
// The factory is the first unit, then one StagedBuild step each. A unit that pumps the
|
||||
// event loop cannot re-enter; a nested call does nothing.
|
||||
bool build_step() override
|
||||
{
|
||||
if (built() || m_building || m_failed)
|
||||
return false;
|
||||
m_building = true;
|
||||
try {
|
||||
if (m_object == nullptr)
|
||||
m_object = m_make();
|
||||
else
|
||||
step();
|
||||
} catch (...) {
|
||||
m_building = false;
|
||||
throw;
|
||||
}
|
||||
m_building = false;
|
||||
if (m_object == nullptr) {
|
||||
m_failed = true;
|
||||
log_null_factory(m_name);
|
||||
return false;
|
||||
}
|
||||
if (steps_remain())
|
||||
return true;
|
||||
m_complete.store(true, std::memory_order_release);
|
||||
for (auto& fn : m_deferred)
|
||||
fn(*m_object);
|
||||
m_deferred.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr bool registered() { return std::is_base_of_v<LazyInstance<T>, T>; }
|
||||
static constexpr bool staged() { return std::is_base_of_v<StagedBuild, T>; }
|
||||
|
||||
bool steps_remain() const
|
||||
{
|
||||
if constexpr (staged())
|
||||
return !m_object->built();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void step()
|
||||
{
|
||||
if constexpr (staged())
|
||||
m_object->build_step();
|
||||
}
|
||||
|
||||
std::string m_name;
|
||||
int m_order;
|
||||
Factory m_make;
|
||||
T* m_object{ nullptr };
|
||||
std::atomic<bool> m_complete{ false };
|
||||
bool m_building{ false };
|
||||
bool m_failed{ false };
|
||||
std::vector<std::function<void(T&)>> m_deferred;
|
||||
};
|
||||
|
||||
// Mixin for a type with one lazily built instance in the app. The statics reach that
|
||||
// instance through its Lazy holder (a recreated MainFrame's holder replaces the old
|
||||
// frame's).
|
||||
template <class Self>
|
||||
class LazyInstance
|
||||
{
|
||||
public:
|
||||
// Null until completely built.
|
||||
static Self* if_built() { return s_lazy ? s_lazy->get() : nullptr; }
|
||||
// Builds the object if needed; null while no holder exists or the holder has no
|
||||
// object.
|
||||
static Self* ensure() { return s_lazy ? s_lazy->ensure() : nullptr; }
|
||||
// Runs fn on the object now if it is built, otherwise once it is.
|
||||
static void when_built(std::function<void(Self&)> fn)
|
||||
{
|
||||
if (s_lazy)
|
||||
s_lazy->when_built(std::move(fn));
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Lazy<Self>;
|
||||
inline static Lazy<Self>* s_lazy{ nullptr };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "LazyPage.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
void apply_dark_ui_to_lazy_panel(wxWindow* panel)
|
||||
{
|
||||
#ifdef _MSW_DARK_MODE
|
||||
wxGetApp().UpdateDarkUIWin(panel);
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <wx/bookctrl.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Applies the app's dark-mode pass to a panel built after the frame's own pass ran.
|
||||
void apply_dark_ui_to_lazy_panel(wxWindow* panel);
|
||||
|
||||
// A page whose panel is built the first time the page is shown, or earlier by an
|
||||
// IdleScheduler; nothing builds while the frame is still hidden. The Lazy side holds the
|
||||
// panel and, for a LazyInstance panel type, registers it for the type's statics.
|
||||
template <class Panel>
|
||||
class LazyPage : public wxPanel, public Lazy<Panel>
|
||||
{
|
||||
public:
|
||||
using Factory = std::function<Panel*(wxWindow* parent)>;
|
||||
|
||||
LazyPage(wxWindow* parent, std::string name, int order, Factory make = [](wxWindow* parent) { return new Panel(parent); })
|
||||
: wxPanel(parent), Lazy<Panel>(std::move(name), order, [this, make = std::move(make)] {
|
||||
Panel* panel = make(this);
|
||||
GetSizer()->Add(panel, 1, wxEXPAND);
|
||||
// Hidden while its page is, as an inserted page would be.
|
||||
if (!IsShown())
|
||||
panel->Hide();
|
||||
return panel;
|
||||
})
|
||||
{
|
||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||
// Shown by the book when its tab is selected.
|
||||
Hide();
|
||||
this->when_built([this](Panel& panel) {
|
||||
apply_dark_ui_to_lazy_panel(&panel);
|
||||
Layout();
|
||||
});
|
||||
}
|
||||
|
||||
// The parent book currently lists this page.
|
||||
bool in_book() const
|
||||
{
|
||||
auto* book = dynamic_cast<wxBookCtrlBase*>(GetParent());
|
||||
return book != nullptr && book->FindPage(this) != wxNOT_FOUND;
|
||||
}
|
||||
|
||||
// Pending only while the tab is in the book.
|
||||
bool pending() const override { return in_book() && Lazy<Panel>::pending(); }
|
||||
|
||||
// Forwarded so the panel's own Show() override stays its activation hook; wx calls this
|
||||
// virtual from the book's ShowWithEffect() only for wxSHOW_EFFECT_NONE, the default.
|
||||
bool Show(bool show = true) override
|
||||
{
|
||||
const bool changed = wxPanel::Show(show);
|
||||
if (show) {
|
||||
// The book shows its first page as it is inserted, before startup has chosen the
|
||||
// start page, so a hidden frame builds nothing; MainFrame::Show() completes it.
|
||||
if (this->built() || wxGetTopLevelParent(this)->IsShown()) {
|
||||
if (Panel* panel = this->ensure())
|
||||
panel->Show(true);
|
||||
// The sizer skipped the panel while the book kept it hidden.
|
||||
Layout();
|
||||
}
|
||||
} else if (Panel* panel = this->get()) {
|
||||
panel->Show(false);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
+229
-170
@@ -81,6 +81,7 @@
|
||||
|
||||
#ifdef __WXGTK__
|
||||
#include <gtk/gtk.h>
|
||||
#include <wx/glcanvas.h>
|
||||
#endif // __WXGTK__
|
||||
#include <slic3r/GUI/CreatePresetsDialog.hpp>
|
||||
|
||||
@@ -317,7 +318,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
// BBS
|
||||
, m_recent_projects(18)
|
||||
, m_settings_dialog(this)
|
||||
, diff_dialog(this)
|
||||
, m_idle([] { return wxGetApp().input_idle_ms(); })
|
||||
, m_diff_dialog("compare_presets", 100, [this] { return make_diff_dialog(); })
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
set_miniaturizable(GetHandle());
|
||||
@@ -509,6 +511,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
wxQueueEvent(wxGetApp().plater(), new SimpleEvent(EVT_NOTICE_CHILDE_SIZE_CHANGED));
|
||||
|
||||
fit_tab_labels(); // ORCA on resize
|
||||
// Restarts the idle build so a hidden Prepare page is laid out at the new size.
|
||||
if (m_prebuild_started)
|
||||
m_idle.start();
|
||||
});
|
||||
|
||||
//BBS
|
||||
@@ -737,9 +742,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
|
||||
|
||||
wxGetApp().persist_window_geometry(this, true);
|
||||
wxGetApp().persist_window_geometry(&m_settings_dialog, true);
|
||||
// bind events from DiffDlg
|
||||
|
||||
bind_diff_dialog();
|
||||
}
|
||||
|
||||
bool MainFrame::handle_global_shortcut(const KeyChord& chord)
|
||||
@@ -832,8 +834,10 @@ bool MainFrame::handle_global_shortcut(const KeyChord& chord)
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainFrame::bind_diff_dialog()
|
||||
DiffPresetDialog* MainFrame::make_diff_dialog()
|
||||
{
|
||||
auto* dialog = new DiffPresetDialog(this);
|
||||
|
||||
auto get_tab = [](Preset::Type type) {
|
||||
Tab* null_tab = nullptr;
|
||||
for (Tab* tab : wxGetApp().tabs_list)
|
||||
@@ -842,23 +846,24 @@ void MainFrame::bind_diff_dialog()
|
||||
return null_tab;
|
||||
};
|
||||
|
||||
auto transfer = [this, get_tab](Preset::Type type) {
|
||||
get_tab(type)->transfer_options(diff_dialog.get_left_preset_name(type),
|
||||
diff_dialog.get_right_preset_name(type),
|
||||
diff_dialog.get_selected_options(type));
|
||||
auto transfer = [dialog, get_tab](Preset::Type type) {
|
||||
get_tab(type)->transfer_options(dialog->get_left_preset_name(type),
|
||||
dialog->get_right_preset_name(type),
|
||||
dialog->get_selected_options(type));
|
||||
};
|
||||
|
||||
auto process_options = [this](std::function<void(Preset::Type)> process) {
|
||||
const Preset::Type diff_dlg_type = diff_dialog.view_type();
|
||||
auto process_options = [dialog](std::function<void(Preset::Type)> process) {
|
||||
const Preset::Type diff_dlg_type = dialog->view_type();
|
||||
if (diff_dlg_type == Preset::TYPE_INVALID) {
|
||||
for (const Preset::Type& type : diff_dialog.types_list() )
|
||||
for (const Preset::Type& type : dialog->types_list() )
|
||||
process(type);
|
||||
}
|
||||
else
|
||||
process(diff_dlg_type);
|
||||
};
|
||||
|
||||
diff_dialog.Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); });
|
||||
dialog->Bind(EVT_DIFF_DIALOG_TRANSFER, [process_options, transfer](SimpleEvent&) { process_options(transfer); });
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -1186,8 +1191,9 @@ void MainFrame::update_edge_panels()
|
||||
void MainFrame::shutdown()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter";
|
||||
if (m_project != nullptr)
|
||||
m_project->shutdown();
|
||||
m_idle.stop();
|
||||
if (ProjectPanel* project = ProjectPanel::if_built())
|
||||
project->shutdown();
|
||||
m_plugin_pages.shutdown();
|
||||
if (m_plater != nullptr)
|
||||
m_plater->remove_dock_panes();
|
||||
@@ -1314,19 +1320,6 @@ void MainFrame::show_option(bool show)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
DesignPanel* MainFrame::ensure_design_panel()
|
||||
{
|
||||
if (m_design_panel == nullptr && m_design_page != nullptr) {
|
||||
wxBusyCursor busy;
|
||||
m_design_panel = new DesignPanel(m_design_page);
|
||||
m_design_page->GetSizer()->Add(m_design_panel, 1, wxEXPAND);
|
||||
m_design_page->Layout();
|
||||
}
|
||||
return m_design_panel;
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainFrame::init_tabpanel() {
|
||||
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on
|
||||
// Windows 10 with multiple high resolution displays connected.
|
||||
@@ -1369,23 +1362,20 @@ void MainFrame::init_tabpanel() {
|
||||
// m_param_panel->OnActivate();
|
||||
#ifdef SLIC3R_CAD
|
||||
else if (m_design_page != nullptr && panel == m_design_page) {
|
||||
// Built on first activation, never at startup: the panel creates several hundred
|
||||
// controls and its own GL canvas, which a user who does not open the tab should
|
||||
// not pay for.
|
||||
ensure_design_panel();
|
||||
// Re-sync the Design bed to the active printer: the panel is built before the
|
||||
// printer profile is fully applied, so its bed must refresh on activation or the
|
||||
// grid (true bed) spills past the stale default bed quad.
|
||||
m_design_panel->on_tab_shown();
|
||||
DesignPanel::ensure()->on_tab_shown();
|
||||
}
|
||||
#endif
|
||||
else if (panel == m_monitor) {
|
||||
else if (panel == m_monitor_page) {
|
||||
//monitor
|
||||
}
|
||||
#ifdef SLIC3R_CAD
|
||||
// Any page that is not Design takes the Design status line down with it — see
|
||||
// DesignPanel::on_tab_hidden for why the popup does not follow the page on its own.
|
||||
if (m_design_panel != nullptr && panel != m_design_page) m_design_panel->on_tab_hidden();
|
||||
if (DesignPanel* design = DesignPanel::if_built(); design != nullptr && panel != m_design_page)
|
||||
design->on_tab_hidden();
|
||||
#endif
|
||||
#ifndef __APPLE__
|
||||
if (m_last_selected_tab == TAB_ID_PREPARE) {
|
||||
@@ -1401,13 +1391,14 @@ void MainFrame::init_tabpanel() {
|
||||
});
|
||||
|
||||
if (wxGetApp().is_editor()) {
|
||||
m_webview = new WebViewPanel(m_tabpanel);
|
||||
m_home_page = new LazyPage<WebViewPanel>(m_tabpanel, TAB_ID_HOME, 10);
|
||||
m_lazy_pages.push_back(m_home_page);
|
||||
Bind(EVT_LOAD_URL, [this](wxCommandEvent &evt) {
|
||||
wxString url = evt.GetString();
|
||||
select_tab(TAB_ID_HOME);
|
||||
m_webview->load_url(url);
|
||||
WebViewPanel::ensure()->load_url(url);
|
||||
});
|
||||
m_tabpanel->AddPage(TAB_ID_HOME, m_webview, "", "tab_home_active");
|
||||
m_tabpanel->AddPage(TAB_ID_HOME, m_home_page, "", "tab_home_active");
|
||||
m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
|
||||
}
|
||||
|
||||
@@ -1418,15 +1409,13 @@ void MainFrame::init_tabpanel() {
|
||||
wxGetApp().plater_ = m_plater;
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
// Stand-in page for the Design tab. The real DesignPanel is built into it the first time
|
||||
// the tab is selected (see the page-changed handler above), so nothing it constructs sits
|
||||
// on the startup path. The experimental feature is off by default, and when it is off the
|
||||
// page is never created, so the tab does not appear at all (the preference takes effect on
|
||||
// the next start, like the other feature toggles).
|
||||
// The experimental feature is off by default, and when it is off the page is never
|
||||
// created, so the tab does not appear at all (the preference takes effect on the next
|
||||
// start, like the other feature toggles).
|
||||
if (wxGetApp().is_enable_cad_feature()) {
|
||||
m_design_page = new wxPanel(this);
|
||||
m_design_page->SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||
m_design_page->Hide();
|
||||
// Experimental and heavy enough that building it unasked would cost more than it saves.
|
||||
m_design_page = new LazyPage<DesignPanel>(this, TAB_ID_DESIGN, -1);
|
||||
m_lazy_pages.push_back(m_design_page);
|
||||
start_mcp_control_if_enabled(); // opens the MCP socket iff ORCA_CAD_MCP is set
|
||||
}
|
||||
#endif
|
||||
@@ -1434,33 +1423,41 @@ void MainFrame::init_tabpanel() {
|
||||
create_preset_tabs();
|
||||
|
||||
//BBS add pages
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor, _L("Device"), "tab_monitor_active");
|
||||
m_monitor_page = new LazyPage<MonitorPanel>(m_tabpanel, TAB_ID_MONITOR, 20);
|
||||
m_lazy_pages.push_back(m_monitor_page);
|
||||
m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor_page, _L("Device"), "tab_monitor_active");
|
||||
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
m_printer_view_page = new LazyPage<PrinterWebView>(m_tabpanel, TAB_ID_MONITOR_WEB, 50, [this](wxWindow* parent) {
|
||||
auto* view = new PrinterWebView(parent);
|
||||
if (!m_printer_url.empty())
|
||||
view->load_url(m_printer_url, m_printer_api_key);
|
||||
return view;
|
||||
});
|
||||
m_lazy_pages.push_back(m_printer_view_page);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
//select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
m_printer_url = evt.GetString();
|
||||
m_printer_api_key = evt.GetAPIkey();
|
||||
if (PrinterWebView* view = PrinterWebView::if_built())
|
||||
view->load_url(m_printer_url, m_printer_api_key);
|
||||
});
|
||||
m_printer_view->Hide();
|
||||
|
||||
m_multi_machine_page = new LazyPage<MultiMachinePage>(m_tabpanel, TAB_ID_MULTI_DEVICE, 40);
|
||||
m_lazy_pages.push_back(m_multi_machine_page);
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
// TODO: change the bitmap
|
||||
m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
|
||||
m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
|
||||
m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_project->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_PROJECT, m_project, _L("Project"), "tab_auxiliary_active");
|
||||
m_project_page = new LazyPage<ProjectPanel>(m_tabpanel, TAB_ID_PROJECT, 60);
|
||||
m_lazy_pages.push_back(m_project_page);
|
||||
m_tabpanel->AddPage(TAB_ID_PROJECT, m_project_page, _L("Project"), "tab_auxiliary_active");
|
||||
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), "tab_calibration_active");
|
||||
// show_device() removes this tab for printers without the Bambu device tab, and the page
|
||||
// then never builds its panel.
|
||||
m_calibration_page = new LazyPage<CalibrationPanel>(m_tabpanel, TAB_ID_CALIBRATION, 30);
|
||||
m_lazy_pages.push_back(m_calibration_page);
|
||||
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration_page, _L("Calibration"), "tab_calibration_active");
|
||||
|
||||
// Plugin pages are appended after the built-in tabs; their ids are namespaced
|
||||
// (plugin.<plugin_key>.<name>) so they can't collide with the built-in TAB_ID_* constants.
|
||||
@@ -1495,67 +1492,44 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
// Remove the extra page before switching to any layout that shouldn't have it.
|
||||
if (!want_web_device_tab) {
|
||||
if ((idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR_WEB)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_printer_agents) {
|
||||
if (!m_monitor) {
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
|
||||
if (m_tabpanel->FindPage(m_monitor) == wxNOT_FOUND) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if (!m_monitor_page->in_book()) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
m_monitor->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
}
|
||||
|
||||
if (m_printer_view == nullptr) {
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
// select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
});
|
||||
}
|
||||
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
if (!m_multi_machine) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
// TODO: change the bitmap
|
||||
if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) {
|
||||
m_multi_machine->Show(false);
|
||||
if (!m_multi_machine_page->in_book()) {
|
||||
m_multi_machine_page->Show(false);
|
||||
// Past the web Device tab when it is already there, so enabling multi-machine
|
||||
// later can't wedge this page between the two Device tabs.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR_WEB, TAB_ID_MONITOR}),
|
||||
TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
|
||||
TAB_ID_MULTI_DEVICE, m_multi_machine_page, _L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
}
|
||||
if (!m_calibration) {
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) {
|
||||
m_calibration->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
|
||||
if (!m_calibration_page->in_book()) {
|
||||
m_calibration_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page,
|
||||
_L("Calibration"), "tab_calibration_active");
|
||||
}
|
||||
|
||||
if (want_web_device_tab) {
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) == wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
// Immediately right of the native Device tab, not at the end of the tab bar.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MONITOR_WEB,
|
||||
m_printer_view, _L("Device (Web)"), "tab_monitor_active");
|
||||
m_printer_view_page, _L("Device (Web)"), "tab_monitor_active");
|
||||
} else {
|
||||
m_tabpanel->SetPageText(idx, _L("Device (Web)"));
|
||||
}
|
||||
@@ -1567,48 +1541,37 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
|
||||
fit_tab_labels(); // ORCA on printer change
|
||||
m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
|
||||
|
||||
if (m_prebuild_started)
|
||||
m_idle.start();
|
||||
return;
|
||||
}
|
||||
|
||||
if (should_use_native) {
|
||||
if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND) {
|
||||
if (m_monitor_page->in_book()) {
|
||||
fit_tab_labels(); // ORCA on printer change - same button layout
|
||||
return;
|
||||
}
|
||||
// Remove printer view
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
|
||||
m_printer_view->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_printer_view_page)) != wxNOT_FOUND) {
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
|
||||
// Create/insert monitor page
|
||||
if (!m_monitor) {
|
||||
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_monitor->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
m_monitor->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
|
||||
// Insert monitor page
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
|
||||
if (wxGetApp().is_enable_multi_machine()) {
|
||||
if (!m_multi_machine) {
|
||||
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_multi_machine->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
// TODO: change the bitmap
|
||||
m_multi_machine->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine,
|
||||
m_multi_machine_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine_page,
|
||||
_L("Multi-device"), "tab_multi_active");
|
||||
}
|
||||
if (!m_calibration) {
|
||||
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_calibration->SetBackgroundColour(*wxWHITE);
|
||||
}
|
||||
m_calibration->Show(false);
|
||||
m_calibration_page->Show(false);
|
||||
// Last of the built-in tabs, but plugin tabs already sit past it — anchor rather than
|
||||
// append, so its position doesn't depend on the relayout() below running afterwards.
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration_page,
|
||||
_L("Calibration"), "tab_calibration_active");
|
||||
|
||||
#ifdef _MSW_DARK_MODE
|
||||
@@ -1616,37 +1579,30 @@ void MainFrame::show_device(bool should_use_native) {
|
||||
#endif // _MSW_DARK_MODE
|
||||
|
||||
} else {
|
||||
if (m_tabpanel->FindPage(m_printer_view) != wxNOT_FOUND) {
|
||||
if (m_printer_view_page->in_book()) {
|
||||
fit_tab_labels(); // ORCA on printer change - same button layout
|
||||
return;
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_calibration)) != wxNOT_FOUND) {
|
||||
m_calibration->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_calibration_page)) != wxNOT_FOUND) {
|
||||
m_calibration_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_multi_machine)) != wxNOT_FOUND) {
|
||||
m_multi_machine->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_multi_machine_page)) != wxNOT_FOUND) {
|
||||
m_multi_machine_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if ((idx = m_tabpanel->FindPage(m_monitor)) != wxNOT_FOUND) {
|
||||
m_monitor->Show(false);
|
||||
if ((idx = m_tabpanel->FindPage(m_monitor_page)) != wxNOT_FOUND) {
|
||||
m_monitor_page->Show(false);
|
||||
m_tabpanel->RemovePage(idx);
|
||||
}
|
||||
if (m_printer_view == nullptr) {
|
||||
m_printer_view = new PrinterWebView(m_tabpanel);
|
||||
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent& evt) {
|
||||
wxString url = evt.GetString();
|
||||
wxString key = evt.GetAPIkey();
|
||||
// select_tab(MainFrame::tpMonitor);
|
||||
m_printer_view->load_url(url, key);
|
||||
});
|
||||
}
|
||||
m_printer_view->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view,
|
||||
m_printer_view_page->Show(false);
|
||||
m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view_page,
|
||||
_L("Device"), "tab_monitor_active");
|
||||
}
|
||||
fit_tab_labels(); // ORCA on printer change
|
||||
m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
|
||||
if (m_prebuild_started)
|
||||
m_idle.start();
|
||||
}
|
||||
|
||||
bool MainFrame::is_prepare_or_preview_tab() const
|
||||
@@ -2692,13 +2648,11 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
|
||||
//BBS GUI refactor: remove unused layout new/dlg
|
||||
//if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog
|
||||
m_param_panel->msw_rescale();
|
||||
m_project->msw_rescale();
|
||||
if(m_monitor)
|
||||
m_monitor->msw_rescale();
|
||||
if(m_multi_machine)
|
||||
m_multi_machine->msw_rescale();
|
||||
if(m_calibration)
|
||||
m_calibration->msw_rescale();
|
||||
// A panel mid-build gets the pass once it is complete.
|
||||
ProjectPanel::when_built([](ProjectPanel& project) { project.msw_rescale(); });
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.msw_rescale(); });
|
||||
MultiMachinePage::when_built([](MultiMachinePage& multi_machine) { multi_machine.msw_rescale(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.msw_rescale(); });
|
||||
|
||||
// BBS
|
||||
#if 0
|
||||
@@ -2752,7 +2706,8 @@ void MainFrame::on_sys_color_changed()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
diff_dialog.on_sys_color_changed();
|
||||
if (DiffPresetDialog* dialog = DiffPresetDialog::if_built())
|
||||
dialog->on_sys_color_changed();
|
||||
|
||||
// BBS
|
||||
m_tabpanel->Rescale();
|
||||
@@ -2760,10 +2715,8 @@ void MainFrame::on_sys_color_changed()
|
||||
|
||||
// update Plater
|
||||
wxGetApp().plater()->sys_color_changed();
|
||||
if(m_monitor)
|
||||
m_monitor->on_sys_color_changed();
|
||||
if(m_calibration)
|
||||
m_calibration->on_sys_color_changed();
|
||||
MonitorPanel::when_built([](MonitorPanel& monitor) { monitor.on_sys_color_changed(); });
|
||||
CalibrationPanel::when_built([](CalibrationPanel& calibration) { calibration.on_sys_color_changed(); });
|
||||
// update Tabs
|
||||
for (auto tab : wxGetApp().tabs_list)
|
||||
tab->sys_color_changed();
|
||||
@@ -3696,7 +3649,8 @@ void MainFrame::set_max_recent_count(int max)
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
wxGetApp().app_config->save();
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4040,23 +3994,124 @@ void MainFrame::select_tab(wxPanel* panel)
|
||||
select_tab(page_name);
|
||||
}
|
||||
|
||||
// Selects the Prepare page without the page-changed event, so the GL canvas is on screen
|
||||
// and nothing else is built for the pass.
|
||||
void MainFrame::select_prepare_for_gl_init()
|
||||
{
|
||||
m_tabpanel->ChangeSelection(m_tabpanel->FindPageByName(TAB_ID_PREPARE));
|
||||
}
|
||||
|
||||
// The book shows its first page as it is inserted, while the frame is hidden and nothing
|
||||
// may build; the first show completes that page.
|
||||
bool MainFrame::Show(bool show)
|
||||
{
|
||||
const bool changed = DPIFrame::Show(show);
|
||||
if (show && changed && m_tabpanel != nullptr)
|
||||
if (wxWindow* page = m_tabpanel->GetCurrentPage())
|
||||
page->Show(true);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool MainFrame::GLResourcesPrebuild::built() const
|
||||
{
|
||||
return m_frame.m_plater != nullptr && m_frame.m_plater->canvas3D()->is_initialized() &&
|
||||
m_frame.m_plater->get_partplate_list().icon_textures_loaded();
|
||||
}
|
||||
|
||||
bool MainFrame::GLResourcesPrebuild::build_step()
|
||||
{
|
||||
GLCanvas3D* canvas = m_frame.m_plater->canvas3D();
|
||||
#ifdef __WXGTK__
|
||||
// wx creates a GTK canvas's GL surface when the widget is realized, so the context can be
|
||||
// made current on it while hidden.
|
||||
gtk_widget_realize(canvas->get_wxglcanvas()->GetHandle());
|
||||
#endif
|
||||
if (!canvas->make_current_for_postinit()) {
|
||||
// The first render of the canvas loads everything instead.
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": cannot make the GL context current on the hidden canvas";
|
||||
m_failed = true;
|
||||
return false;
|
||||
}
|
||||
switch (m_step) {
|
||||
case 0:
|
||||
m_failed = !wxGetApp().init_opengl();
|
||||
break;
|
||||
case 1: {
|
||||
const Size size = canvas->get_canvas_size();
|
||||
wxGetApp().imgui()->set_display_size(float(std::max(1, size.get_width())), float(std::max(1, size.get_height())));
|
||||
canvas->set_imgui_scaling();
|
||||
// Builds the font atlas without leaving a frame open at the hidden canvas's size.
|
||||
wxGetApp().imgui()->new_frame();
|
||||
wxGetApp().imgui()->end_frame();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
// One texture per unit until none remain.
|
||||
if (m_frame.m_plater->get_partplate_list().load_next_plate_texture())
|
||||
return true;
|
||||
break;
|
||||
case 3:
|
||||
m_failed = !canvas->init();
|
||||
break;
|
||||
default:
|
||||
// Runs after init(), which sets the color mode the icons are drawn for.
|
||||
m_frame.m_plater->get_partplate_list().load_icon_textures();
|
||||
return false;
|
||||
}
|
||||
++m_step;
|
||||
return !m_failed;
|
||||
}
|
||||
|
||||
bool MainFrame::PrepareLayoutPrebuild::built() const
|
||||
{
|
||||
// The book lays out the page it shows.
|
||||
const wxWindow* page = m_frame.m_tabpanel != nullptr ? m_frame.m_tabpanel->GetCurrentPage() : nullptr;
|
||||
return page == nullptr || page == m_frame.m_plater || page->GetSize() == m_laid_out_size;
|
||||
}
|
||||
|
||||
bool MainFrame::PrepareLayoutPrebuild::build_step()
|
||||
{
|
||||
// Sized as the book sizes the page it selects, so the selection finds nothing to lay out.
|
||||
const wxWindow* page = m_frame.m_tabpanel->GetCurrentPage();
|
||||
m_laid_out_size = page->GetSize();
|
||||
m_frame.m_plater->SetSize(page->GetRect());
|
||||
return false;
|
||||
}
|
||||
|
||||
// A page out of the book stays registered and is passed over; a negative order is never
|
||||
// registered.
|
||||
void MainFrame::prebuild_pages_when_idle()
|
||||
{
|
||||
m_idle.clear();
|
||||
m_idle.add(m_gl_prebuild);
|
||||
if (m_param_panel)
|
||||
m_idle.add(m_param_panel->settings_page_prebuild());
|
||||
m_idle.add(m_prepare_layout_prebuild);
|
||||
for (LazyBase* page : m_lazy_pages)
|
||||
if (page->prebuild_order() >= 0)
|
||||
m_idle.add(*page);
|
||||
m_idle.add(m_diff_dialog);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": prebuild queue: " << m_idle.names();
|
||||
m_idle.start();
|
||||
m_prebuild_started = true;
|
||||
}
|
||||
|
||||
//BBS
|
||||
void MainFrame::jump_to_monitor(std::string dev_id)
|
||||
{
|
||||
if(!m_monitor)
|
||||
return;
|
||||
m_tabpanel->SelectPageByName(TAB_ID_MONITOR);
|
||||
if (!dev_id.empty()) {
|
||||
((MonitorPanel*)m_monitor)->select_machine(dev_id);
|
||||
MonitorPanel::ensure()->select_machine(dev_id);
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::jump_to_multipage()
|
||||
{
|
||||
if(!m_multi_machine)
|
||||
if (!m_multi_machine_page->in_book())
|
||||
return;
|
||||
m_tabpanel->SelectPageByName(TAB_ID_MULTI_DEVICE);
|
||||
((MultiMachinePage*)m_multi_machine)->jump_to_send_page();
|
||||
if (MultiMachinePage* page = m_multi_machine_page->ensure())
|
||||
page->jump_to_send_page();
|
||||
}
|
||||
|
||||
|
||||
@@ -4105,8 +4160,8 @@ void MainFrame::request_select_tab(const wxString& id)
|
||||
}
|
||||
|
||||
int MainFrame::get_calibration_curr_tab() {
|
||||
if (m_calibration)
|
||||
return m_calibration->get_tabpanel()->GetSelection();
|
||||
if (CalibrationPanel* calibration = CalibrationPanel::if_built())
|
||||
return calibration->get_tabpanel()->GetSelection();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4215,7 +4270,8 @@ void MainFrame::add_to_recent_projects(const wxString& filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(0);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4331,7 +4387,8 @@ void MainFrame::open_recent_project(size_t file_id, wxString const & filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4354,7 +4411,8 @@ void MainFrame::remove_recent_project(size_t file_id, wxString const &filename)
|
||||
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
|
||||
}
|
||||
wxGetApp().app_config->set_recent_projects(recent_projects);
|
||||
m_webview->SendRecentList(-1);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendRecentList(-1);
|
||||
}
|
||||
|
||||
void MainFrame::load_url(wxString url)
|
||||
@@ -4408,14 +4466,14 @@ bool MainFrame::is_printer_view() const { return m_tabpanel->GetSelectedPageName
|
||||
|
||||
void MainFrame::refresh_plugin_tips()
|
||||
{
|
||||
if (m_webview != nullptr)
|
||||
m_webview->ShowNetpluginTip();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->ShowNetpluginTip();
|
||||
}
|
||||
|
||||
void MainFrame::RunScript(wxString js)
|
||||
{
|
||||
if (m_webview != nullptr)
|
||||
m_webview->RunScript(js);
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->RunScript(js);
|
||||
}
|
||||
|
||||
void MainFrame::technology_changed()
|
||||
@@ -4524,7 +4582,8 @@ void MainFrame::update_side_preset_ui()
|
||||
|
||||
|
||||
//take off multi machine
|
||||
if(m_multi_machine){m_multi_machine->clear_page();}
|
||||
if (MultiMachinePage* multi_machine = MultiMachinePage::if_built())
|
||||
multi_machine->clear_page();
|
||||
}
|
||||
|
||||
void MainFrame::on_select_default_preset(SimpleEvent& evt)
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "Widgets/SideButton.hpp"
|
||||
#include "Widgets/SideMenuPopup.hpp"
|
||||
#include "FilamentGroupPopup.hpp"
|
||||
#include "LazyPage.hpp"
|
||||
#include "IdleScheduler.hpp"
|
||||
|
||||
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
@@ -136,6 +138,45 @@ class MainFrame : public DPIFrame
|
||||
#endif
|
||||
bool m_loaded {false};
|
||||
wxTimer* m_reset_title_text_colour_timer{ nullptr };
|
||||
IdleScheduler m_idle;
|
||||
bool m_prebuild_started{ false };
|
||||
// Loads the Prepare canvas's GL resources while its page is hidden.
|
||||
class GLResourcesPrebuild : public LazyBase
|
||||
{
|
||||
public:
|
||||
explicit GLResourcesPrebuild(MainFrame& frame) : m_frame(frame) {}
|
||||
const std::string& name() const override { return m_name; }
|
||||
bool built() const override;
|
||||
bool pending() const override { return !m_failed && !built(); }
|
||||
bool build_step() override;
|
||||
int prebuild_order() const override { return 0; }
|
||||
|
||||
private:
|
||||
MainFrame& m_frame;
|
||||
std::string m_name{ "gl_resources" };
|
||||
int m_step{ 0 };
|
||||
bool m_failed{ false };
|
||||
} m_gl_prebuild{ *this };
|
||||
// Lays out the hidden Prepare page at the size the book gives its pages.
|
||||
class PrepareLayoutPrebuild : public LazyBase
|
||||
{
|
||||
public:
|
||||
explicit PrepareLayoutPrebuild(MainFrame& frame) : m_frame(frame) {}
|
||||
const std::string& name() const override { return m_name; }
|
||||
bool built() const override;
|
||||
bool build_step() override;
|
||||
int prebuild_order() const override { return 0; }
|
||||
|
||||
private:
|
||||
MainFrame& m_frame;
|
||||
std::string m_name{ "prepare_layout" };
|
||||
wxSize m_laid_out_size;
|
||||
} m_prepare_layout_prebuild{ *this };
|
||||
// Every LazyPage, in and out of the book; prebuild_pages_when_idle() registers them.
|
||||
std::vector<LazyBase*> m_lazy_pages;
|
||||
// The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built.
|
||||
wxString m_printer_url;
|
||||
wxString m_printer_api_key;
|
||||
|
||||
wxString m_qs_last_input_file = wxEmptyString;
|
||||
wxString m_qs_last_output_file = wxEmptyString;
|
||||
@@ -180,7 +221,7 @@ class MainFrame : public DPIFrame
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_reslice() const;
|
||||
void bind_diff_dialog();
|
||||
DiffPresetDialog* make_diff_dialog();
|
||||
|
||||
// BBS
|
||||
wxBoxSizer* create_side_tools();
|
||||
@@ -377,6 +418,12 @@ public:
|
||||
void select_tab(wxPanel* panel);
|
||||
void select_tab(const wxString& id = wxString());
|
||||
void request_select_tab(const wxString& id);
|
||||
// post_init() needs the plater's canvas on screen to initialize OpenGL; this pass does not
|
||||
// build the settings page.
|
||||
void select_prepare_for_gl_init();
|
||||
// Builds the lazy tab pages while the user is idle; post_init() calls it once.
|
||||
void prebuild_pages_when_idle();
|
||||
bool Show(bool show = true) override;
|
||||
int get_calibration_curr_tab();
|
||||
void select_view(const std::string& direction);
|
||||
void update_shortcut_labels();
|
||||
@@ -435,27 +482,21 @@ public:
|
||||
BBLTopbar* m_topbar{ nullptr };
|
||||
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||
Plater* m_plater { nullptr };
|
||||
// Lazy pages, created once and kept for the frame's life; their panels are reached
|
||||
// through LazyInstance's statics, and show_device() only moves pages in and out of the book.
|
||||
#ifdef SLIC3R_CAD
|
||||
// The tab page is the placeholder; m_design_panel stays null until the tab is first
|
||||
// selected, so everything the Design panel builds stays off the startup path.
|
||||
wxPanel* m_design_page { nullptr };
|
||||
DesignPanel* m_design_panel { nullptr };
|
||||
// Builds the Design panel if it does not exist yet and returns it (null only before the
|
||||
// placeholder page itself exists). Main thread only -- it creates wx controls. Both the
|
||||
// tab activation and the MCP socket go through this: the socket is driven headlessly,
|
||||
// with nobody to click the tab, and without this every verb would answer "not ready".
|
||||
DesignPanel* ensure_design_panel();
|
||||
LazyPage<DesignPanel>* m_design_page { nullptr };
|
||||
#endif
|
||||
//BBS: GUI refactor
|
||||
MonitorPanel* m_monitor{ nullptr };
|
||||
LazyPage<MonitorPanel>* m_monitor_page{ nullptr };
|
||||
|
||||
//AuxiliaryPanel* m_auxiliary{ nullptr };
|
||||
MultiMachinePage* m_multi_machine{ nullptr };
|
||||
ProjectPanel* m_project{ nullptr };
|
||||
LazyPage<MultiMachinePage>* m_multi_machine_page{ nullptr };
|
||||
LazyPage<ProjectPanel>* m_project_page{ nullptr };
|
||||
|
||||
CalibrationPanel* m_calibration{ nullptr };
|
||||
WebViewPanel* m_webview { nullptr };
|
||||
PrinterWebView* m_printer_view{nullptr};
|
||||
LazyPage<CalibrationPanel>* m_calibration_page{ nullptr };
|
||||
LazyPage<WebViewPanel>* m_home_page { nullptr };
|
||||
LazyPage<PrinterWebView>* m_printer_view_page{nullptr};
|
||||
PluginPages m_plugin_pages;
|
||||
wxLogWindow* m_log_window { nullptr };
|
||||
// BBS
|
||||
@@ -466,7 +507,8 @@ public:
|
||||
ParamsDialog* m_param_dialog{ nullptr };
|
||||
//BBS
|
||||
SettingsDialog m_settings_dialog;
|
||||
DiffPresetDialog diff_dialog;
|
||||
// The Compare presets dialog, built on first use or at idle through its holder.
|
||||
Lazy<DiffPresetDialog> m_diff_dialog;
|
||||
wxWindow* m_plater_page{ nullptr };
|
||||
PrintHostQueueDialog* m_printhost_queue_dlg;
|
||||
|
||||
|
||||
@@ -1125,7 +1125,7 @@ wxBoxSizer* MixedFilamentDialog::create_recommendation_grid()
|
||||
outer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(4));
|
||||
|
||||
m_recommendation_scroll = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(116)));
|
||||
m_recommendation_scroll->SetScrollRate(0, 5);
|
||||
m_recommendation_scroll->SetScrollRate(0, FromDIP(20));
|
||||
m_recommendation_scroll->SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#F8F8F8")));
|
||||
|
||||
m_recommendation_grid = new wxWrapSizer(wxHORIZONTAL, wxREMOVE_LEADING_SPACES);
|
||||
|
||||
+44
-37
@@ -105,6 +105,7 @@ MonitorPanel::MonitorPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos,
|
||||
: wxPanel(parent, id, pos, size, style),
|
||||
m_select_machine(SelectMachinePopup(this))
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
#ifdef __WINDOWS__
|
||||
SetDoubleBuffered(true);
|
||||
#endif //__WINDOWS__
|
||||
@@ -182,31 +183,36 @@ void MonitorPanel::init_tabpanel()
|
||||
auto title = m_tabpanel->GetPageText(m_tabpanel->GetSelection());
|
||||
m_media_file_panel->SwitchStorage(title == _L("Storage"));
|
||||
}
|
||||
page->SetFocus();
|
||||
// The first page is selected while the panel is built off screen.
|
||||
if (page->IsShownOnScreen())
|
||||
page->SetFocus();
|
||||
update_all();
|
||||
}, m_tabpanel->GetId());
|
||||
|
||||
//m_status_add_machine_panel = new AddMachinePanel(m_tabpanel);
|
||||
m_status_info_panel = new StatusPanel(m_tabpanel);
|
||||
m_status_info_panel = new StatusPanel(m_tabpanel);
|
||||
// Queued before the page is added, since adding it selects it and runs the handler above,
|
||||
// where built() must already be false.
|
||||
add_build_steps_of(*m_status_info_panel);
|
||||
m_tabpanel->AddPage(m_status_info_panel, _L("Status"), true);
|
||||
add_build_step([this] {
|
||||
m_media_file_panel = new MediaFilePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_upgrade_panel = new UpgradePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false);
|
||||
});
|
||||
add_build_step([this] {
|
||||
m_hms_panel = new HMSPanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false);
|
||||
|
||||
m_media_file_panel = new MediaFilePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_media_file_panel, _L("Storage"), false);
|
||||
//m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), false);
|
||||
std::string network_ver = Slic3r::NetworkAgent::get_version();
|
||||
if (!network_ver.empty()) {
|
||||
m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver));
|
||||
}
|
||||
|
||||
m_upgrade_panel = new UpgradePanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_upgrade_panel, _L_CONTEXT(L_CONTEXT("Update", "Firmware"), "Firmware"), false);
|
||||
|
||||
m_hms_panel = new HMSPanel(m_tabpanel);
|
||||
m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false);
|
||||
|
||||
std::string network_ver = Slic3r::NetworkAgent::get_version();
|
||||
if (!network_ver.empty()) {
|
||||
m_tabpanel->SetFooterText(wxString::Format(_L("Network plug-in v%s"), network_ver));
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
show_status((int)MonitorStatus::MONITOR_NO_PRINTER);
|
||||
show_status((int)MonitorStatus::MONITOR_NO_PRINTER);
|
||||
});
|
||||
}
|
||||
|
||||
void MonitorPanel::set_default()
|
||||
@@ -298,19 +304,23 @@ void MonitorPanel::on_select_printer(wxCommandEvent& event)
|
||||
set_default();
|
||||
update_all();
|
||||
|
||||
MachineObject *obj_ = dev->get_selected_machine();
|
||||
if (obj_) {
|
||||
obj_->last_cali_version = -1;
|
||||
obj_->reset_pa_cali_history_result();
|
||||
obj_->reset_pa_cali_result();
|
||||
Sidebar &sidebar = GUI::wxGetApp().sidebar();
|
||||
sidebar.update_sync_status(obj_);
|
||||
sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj_));
|
||||
}
|
||||
on_machine_selected(dev->get_selected_machine());
|
||||
|
||||
Layout();
|
||||
}
|
||||
|
||||
void MonitorPanel::on_machine_selected(MachineObject* obj)
|
||||
{
|
||||
if (obj == nullptr)
|
||||
return;
|
||||
obj->last_cali_version = -1;
|
||||
obj->reset_pa_cali_history_result();
|
||||
obj->reset_pa_cali_result();
|
||||
Sidebar& sidebar = GUI::wxGetApp().sidebar();
|
||||
sidebar.update_sync_status(obj);
|
||||
sidebar.set_need_auto_sync_after_connect_printer(sidebar.need_auto_sync_extruder_list_after_connect_priner(obj));
|
||||
}
|
||||
|
||||
void MonitorPanel::on_printer_clicked(wxMouseEvent &event)
|
||||
{
|
||||
auto mouse_pos = ClientToScreen(event.GetPosition());
|
||||
@@ -341,7 +351,8 @@ void MonitorPanel::on_size(wxSizeEvent &event)
|
||||
|
||||
void MonitorPanel::update_all()
|
||||
{
|
||||
if (!m_initialized)
|
||||
// Every page exists once the last build step has run.
|
||||
if (!built())
|
||||
return;
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
@@ -422,16 +433,12 @@ void MonitorPanel::update_hms_tag()
|
||||
|
||||
bool MonitorPanel::Show(bool show)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// Notebook::InsertPage() hides every page it appends, so this also runs while MainFrame is
|
||||
// still constructing, before GUI_App::mainframe is assigned. Same guard as Plater::Show().
|
||||
if (wxGetApp().mainframe)
|
||||
wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize());
|
||||
#endif
|
||||
|
||||
NetworkAgent* m_agent = wxGetApp().getAgent();
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (show) {
|
||||
#ifdef __APPLE__
|
||||
wxGetApp().mainframe->SetMinSize(wxGetApp().plater()->GetMinSize());
|
||||
#endif
|
||||
start_update();
|
||||
update_network_version_footer();
|
||||
|
||||
@@ -458,7 +465,7 @@ bool MonitorPanel::Show(bool show)
|
||||
|
||||
void MonitorPanel::show_status(int status)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
if (!built()) return;
|
||||
if (last_status == status)return;
|
||||
if ((last_status & (int)MonitorStatus::MONITOR_CONNECTING) != 0) {
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "slic3r/GUI/AmsWidgets.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
#include "SelectMachinePop.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -72,16 +73,16 @@ public:
|
||||
void msw_rescale();
|
||||
};
|
||||
|
||||
class MonitorPanel : public wxPanel
|
||||
class MonitorPanel : public wxPanel, public StagedBuild, public LazyInstance<MonitorPanel>
|
||||
{
|
||||
private:
|
||||
Tabbook* m_tabpanel{ nullptr };
|
||||
wxSizer* m_main_sizer{ nullptr };
|
||||
|
||||
StatusPanel* m_status_info_panel;
|
||||
MediaFilePanel* m_media_file_panel;
|
||||
UpgradePanel* m_upgrade_panel;
|
||||
HMSPanel* m_hms_panel;
|
||||
StatusPanel* m_status_info_panel{ nullptr };
|
||||
MediaFilePanel* m_media_file_panel{ nullptr };
|
||||
UpgradePanel* m_upgrade_panel{ nullptr };
|
||||
HMSPanel* m_hms_panel{ nullptr };
|
||||
|
||||
/* side tools */
|
||||
SideTools* m_side_tools{nullptr};
|
||||
@@ -96,7 +97,6 @@ private:
|
||||
wxBitmap m_arrow_img;
|
||||
|
||||
int last_status;
|
||||
bool m_initialized { false };
|
||||
bool update_flag{false};
|
||||
wxTimer* m_refresh_timer = nullptr;
|
||||
|
||||
@@ -125,6 +125,8 @@ public:
|
||||
|
||||
StatusPanel* get_status_panel() {return m_status_info_panel;};
|
||||
void select_machine(std::string machine_sn);
|
||||
// Resets the selected printer's calibration results and syncs the sidebar, without the Device tab.
|
||||
static void on_machine_selected(MachineObject* obj);
|
||||
void on_timer(wxTimerEvent& event);
|
||||
void on_select_printer(wxCommandEvent& event);
|
||||
void on_printer_clicked(wxMouseEvent &event);
|
||||
|
||||
@@ -194,9 +194,10 @@ Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, con
|
||||
bd->button = btn;
|
||||
//bd->type = type;
|
||||
|
||||
mb->id = wxString::Format("%d", m_buttons.size());
|
||||
// m_buttons.size() is a size_t, which does not match the %d conversion
|
||||
mb->id = wxString(std::to_string(m_buttons.size()));
|
||||
mb->buttondata = bd;
|
||||
m_buttons[ wxString::Format("%d", m_buttons.size())] = mb;
|
||||
m_buttons[ wxString(std::to_string(m_buttons.size()))] = mb;
|
||||
return btn;
|
||||
};
|
||||
|
||||
@@ -398,7 +399,7 @@ static void add_msg_content(wxWindow *parent,
|
||||
}
|
||||
wxScrolledWindow *scrolledWindow = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
scrolledWindow->SetScrollRate(0, 20);
|
||||
scrolledWindow->SetScrollRate(0, scrolledWindow->FromDIP(20));
|
||||
scrolledWindow->EnableScrolling(false, true);
|
||||
wxBoxSizer *sizer_scrolled = new wxBoxSizer(wxHORIZONTAL);
|
||||
Label *wrapped_text = new Label(scrolledWindow, font, msg, LB_AUTO_WRAP, wxSize(info_width, -1));
|
||||
|
||||
@@ -21,8 +21,9 @@ MultiMachineItem::MultiMachineItem(wxWindow* parent, MachineObject* obj)
|
||||
Bind(wxEVT_MOTION, &MultiMachineItem::OnMove, this);
|
||||
Bind(EVT_MULTI_DEVICE_VIEW, [obj](auto& e) {
|
||||
wxGetApp().mainframe->jump_to_monitor(obj->get_dev_id());
|
||||
if (wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()) {
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play();
|
||||
MonitorPanel* monitor = MonitorPanel::if_built();
|
||||
if (monitor && monitor->get_status_panel()->get_media_play_ctrl()) {
|
||||
monitor->get_status_panel()->get_media_play_ctrl()->jump_to_play();
|
||||
}
|
||||
});
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
@@ -397,7 +398,7 @@ MultiMachineManagerPage::MultiMachineManagerPage(wxWindow* parent)
|
||||
|
||||
m_machine_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_machine_list->SetBackgroundColour(*wxWHITE);
|
||||
m_machine_list->SetScrollRate(0, 5);
|
||||
m_machine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
|
||||
m_machine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
m_machine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace GUI {
|
||||
MultiMachinePage::MultiMachinePage(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
init_tabpanel();
|
||||
m_main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxLEFT, 0);
|
||||
@@ -322,7 +323,7 @@ MultiMachinePickPage::MultiMachinePickPage(Plater* plater /*= nullptr*/)
|
||||
scroll_macine_list->SetMinSize(wxSize(FromDIP(400), FromDIP(10 * 30)));
|
||||
scroll_macine_list->SetMaxSize(wxSize(FromDIP(400), FromDIP(10 * 30)));
|
||||
scroll_macine_list->SetBackgroundColour(*wxWHITE);
|
||||
scroll_macine_list->SetScrollRate(0, 5);
|
||||
scroll_macine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
|
||||
|
||||
sizer_machine_list = new wxBoxSizer(wxVERTICAL);
|
||||
scroll_macine_list->SetSizer(sizer_machine_list);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "MultiTaskManagerPage.hpp"
|
||||
#include "MultiMachineManagerPage.hpp"
|
||||
#include "Tabbook.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#include "wx/button.h"
|
||||
|
||||
@@ -19,7 +20,7 @@ namespace GUI {
|
||||
#define PICK_LEFT_DEV_STATUS 250
|
||||
#define PICK_DEVICE_MAX 6
|
||||
|
||||
class MultiMachinePage : public wxPanel
|
||||
class MultiMachinePage : public wxPanel, public LazyInstance<MultiMachinePage>
|
||||
{
|
||||
private:
|
||||
wxTimer* m_refresh_timer = nullptr;
|
||||
|
||||
@@ -675,7 +675,7 @@ LocalTaskManagerPage::LocalTaskManagerPage(wxWindow* parent)
|
||||
|
||||
m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_task_list->SetBackgroundColour(*wxWHITE);
|
||||
m_task_list->SetScrollRate(0, 5);
|
||||
m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
|
||||
m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
|
||||
@@ -1056,7 +1056,7 @@ CloudTaskManagerPage::CloudTaskManagerPage(wxWindow* parent)
|
||||
|
||||
m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
m_task_list->SetBackgroundColour(*wxWHITE);
|
||||
m_task_list->SetScrollRate(0, 5);
|
||||
m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT));
|
||||
m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT)));
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ ObjColorPanel::ObjColorPanel(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, c
|
||||
//new color table
|
||||
m_scrolledWindow = new wxScrolledWindow(m_page_simple, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolledWindow->SetScrollRate(0, 20);
|
||||
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
|
||||
m_scrolledWindow->EnableScrolling(false, true);
|
||||
m_scrolledWindow->ShowScrollbars(wxScrollbarVisibility::wxSHOW_SB_NEVER, wxScrollbarVisibility::wxSHOW_SB_DEFAULT);
|
||||
draw_new_table();
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
bool split_multi_line{false};
|
||||
bool option_label_at_right{false};
|
||||
// BBS: new layout
|
||||
wxWindow * stb;
|
||||
wxWindow * stb{ nullptr };
|
||||
const wxString icon;
|
||||
const wxString title;
|
||||
bool m_labels_hidden{false};
|
||||
|
||||
@@ -290,7 +290,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
|
||||
|
||||
m_compare_btn = new ScalableButton(m_top_panel, wxID_ANY, "compare", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_compare_btn->SetToolTip(_L("Compare presets"));
|
||||
m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { wxGetApp().mainframe->diff_dialog.show(); }));
|
||||
m_compare_btn->Bind(wxEVT_BUTTON, ([](wxCommandEvent e) { DiffPresetDialog::ensure()->show(); }));
|
||||
|
||||
m_setting_btn = new ScalableButton(m_top_panel, wxID_ANY, "table", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
|
||||
m_setting_btn->SetToolTip(_L("View all object's settings"));
|
||||
@@ -391,7 +391,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
|
||||
m_page_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_page_view->SetSizer(m_page_sizer);
|
||||
m_page_view->SetScrollbars(1, 20, 1, 2);
|
||||
m_page_view->SetScrollbars(1, FromDIP(20), 1, 2);
|
||||
//m_page_view->SetScrollRate( 5, 5 );
|
||||
|
||||
if (m_mode_region)
|
||||
@@ -550,18 +550,36 @@ void ParamsPanel::clear_page()
|
||||
void ParamsPanel::OnActivate()
|
||||
{
|
||||
if (m_current_tab == NULL)
|
||||
{
|
||||
//the first time
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print");
|
||||
// BBS: open/close tab
|
||||
//m_current_tab = m_tab_print;
|
||||
set_active_tab(m_tab_print ? m_tab_print : m_tab_filament);
|
||||
}
|
||||
select_default_tab();
|
||||
Tab* cur_tab = dynamic_cast<Tab *> (m_current_tab);
|
||||
if (cur_tab)
|
||||
cur_tab->OnActivate();
|
||||
}
|
||||
|
||||
void ParamsPanel::select_default_tab()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": first time opened, set current tab to print");
|
||||
// BBS: open/close tab
|
||||
//m_current_tab = m_tab_print;
|
||||
set_active_tab(m_tab_print ? m_tab_print : m_tab_filament);
|
||||
}
|
||||
|
||||
bool ParamsPanel::SettingsPagePrebuild::built() const
|
||||
{
|
||||
Tab* tab = dynamic_cast<Tab*>(m_panel.m_current_tab);
|
||||
return tab != nullptr && !tab->page_build_pending();
|
||||
}
|
||||
|
||||
bool ParamsPanel::SettingsPagePrebuild::build_step()
|
||||
{
|
||||
if (m_panel.m_current_tab == nullptr) {
|
||||
m_panel.select_default_tab();
|
||||
return !built();
|
||||
}
|
||||
Tab* tab = dynamic_cast<Tab*>(m_panel.m_current_tab);
|
||||
return tab != nullptr && tab->page_build_step();
|
||||
}
|
||||
|
||||
void ParamsPanel::OnToggled(wxCommandEvent& event)
|
||||
{
|
||||
if (m_mode_region && m_mode_region->GetId() == event.GetId()) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
class ModeSwitchButton;
|
||||
class SwitchButton;
|
||||
@@ -121,6 +122,22 @@ class ParamsPanel : public wxPanel
|
||||
|
||||
wxPanel* m_current_tab { nullptr };
|
||||
|
||||
// Builds the selected page's option groups at idle and then shows them for the mode;
|
||||
// while no tab is selected yet, its first unit selects the default one.
|
||||
class SettingsPagePrebuild : public LazyBase
|
||||
{
|
||||
public:
|
||||
explicit SettingsPagePrebuild(ParamsPanel& panel) : m_panel(panel) {}
|
||||
const std::string& name() const override { return m_name; }
|
||||
bool built() const override;
|
||||
bool build_step() override;
|
||||
int prebuild_order() const override { return 0; }
|
||||
|
||||
private:
|
||||
ParamsPanel& m_panel;
|
||||
std::string m_name{ "settings_page" };
|
||||
} m_settings_page_prebuild{ *this };
|
||||
|
||||
bool m_has_object_config { false };
|
||||
|
||||
struct Highlighter
|
||||
@@ -149,6 +166,10 @@ class ParamsPanel : public wxPanel
|
||||
//clear the right page
|
||||
void clear_page();
|
||||
void OnActivate();
|
||||
// The print tab, or the filament tab without one.
|
||||
void select_default_tab();
|
||||
// The settings page's prebuild task.
|
||||
LazyBase& settings_page_prebuild() { return m_settings_page_prebuild; }
|
||||
void set_active_tab(wxPanel*tab);
|
||||
bool is_active_and_shown_tab(wxPanel*tab);
|
||||
void update_mode();
|
||||
|
||||
+130
-125
@@ -799,67 +799,8 @@ void PartPlate::render_logo(bool bottom, bool render_cali)
|
||||
{
|
||||
if (!m_partplate_list->render_bedtype_logo) {
|
||||
// render third-party printer texture logo
|
||||
if (m_partplate_list->m_logo_texture_filename.empty()) {
|
||||
m_partplate_list->m_logo_texture.reset();
|
||||
if (!m_partplate_list->load_logo_texture())
|
||||
return;
|
||||
}
|
||||
|
||||
//GLTexture* temp_texture = const_cast<GLTexture*>(&m_temp_texture);
|
||||
|
||||
if (m_partplate_list->m_logo_texture.get_id() == 0 || m_partplate_list->m_logo_texture.get_source() != m_partplate_list->m_logo_texture_filename) {
|
||||
m_partplate_list->m_logo_texture.reset();
|
||||
|
||||
if (boost::algorithm::iends_with(m_partplate_list->m_logo_texture_filename, ".svg")) {
|
||||
/*// use higher resolution images if graphic card and opengl version allow
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
canvas.request_extra_frame();
|
||||
}*/
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048;
|
||||
if (!m_partplate_list->m_logo_texture.load_from_svg_file(m_partplate_list->m_logo_texture_filename, true, true, true, logo_tex_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_partplate_list->m_logo_texture_filename;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (boost::algorithm::iends_with(m_partplate_list->m_logo_texture_filename, ".png")) {
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
/* if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_logo_texture_filename) {
|
||||
if (!temp_texture->load_from_file(m_logo_texture_filename, false, GLTexture::None, false)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
canvas.request_extra_frame();
|
||||
}*/
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!m_partplate_list->m_logo_texture.load_from_file(m_partplate_list->m_logo_texture_filename, true, GLTexture::MultiThreaded, true)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_partplate_list->m_logo_texture_filename;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not load logo texture from %1%, unsupported format") % m_partplate_list->m_logo_texture_filename;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_partplate_list->m_logo_texture.unsent_compressed_data_available()) {
|
||||
// sends to gpu the already available compressed levels of the main texture
|
||||
m_partplate_list->m_logo_texture.send_compressed_data_to_gpu();
|
||||
|
||||
// the temporary texture is not needed anymore, reset it
|
||||
//if (temp_texture->get_id() != 0)
|
||||
// temp_texture->reset();
|
||||
|
||||
//canvas.request_extra_frame();
|
||||
}
|
||||
|
||||
if (m_logo_triangles.is_initialized())
|
||||
render_logo_texture(m_partplate_list->m_logo_texture, m_logo_triangles, bottom);
|
||||
@@ -3555,8 +3496,11 @@ void PartPlate::render(const Transform3d& view_matrix, const Transform3d& projec
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
if (wxGetApp().show_plate_gridlines() && show_grid)
|
||||
if (wxGetApp().show_plate_gridlines() && show_grid) {
|
||||
glsafe(::glDepthMask(bottom ? GL_TRUE : GL_FALSE));
|
||||
render_grid(bottom);
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
|
||||
if (!hide_chrome && !bottom && m_selected && !force_background_color) {
|
||||
if (m_partplate_list)
|
||||
@@ -4229,6 +4173,7 @@ Vec2d PartPlateList::compute_shape_position(int index, int cols)
|
||||
//generate icon textures
|
||||
void PartPlateList::generate_icon_textures()
|
||||
{
|
||||
m_icon_textures_dark = m_is_dark;
|
||||
// use higher resolution images if graphic card and opengl version allow
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(), icon_size = max_tex_size / 8;
|
||||
std::string path = resources_dir() + "/images/";
|
||||
@@ -4444,6 +4389,9 @@ void PartPlateList::release_icon_textures()
|
||||
PartPlateList::is_load_bedtype_textures = false;
|
||||
PartPlateList::is_load_extruder_only_area_textures = false;
|
||||
PartPlateList::is_load_cali_texture = false;
|
||||
m_next_bedtype_texture = 0;
|
||||
m_next_extruder_only_area_texture = 0;
|
||||
m_next_cali_texture = 0;
|
||||
for (int i = 0; i < btCount; i++) {
|
||||
for (auto& part: bed_texture_info[i].parts) {
|
||||
if (part.texture) {
|
||||
@@ -5999,12 +5947,7 @@ void PartPlateList::render(const Transform3d& view_matrix, const Transform3d& pr
|
||||
plate_hover_action = hover_id % PartPlate::GRABBER_COUNT;
|
||||
}
|
||||
|
||||
static bool last_dark_mode_status = m_is_dark;
|
||||
if (m_is_dark != last_dark_mode_status) {
|
||||
last_dark_mode_status = m_is_dark;
|
||||
generate_icon_textures();
|
||||
} else if(m_del_texture.get_id() == 0)
|
||||
generate_icon_textures();
|
||||
load_icon_textures();
|
||||
for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) {
|
||||
int current_index = (*it)->get_index();
|
||||
if (only_current && (current_index != m_current_plate))
|
||||
@@ -6146,6 +6089,8 @@ bool PartPlateList::set_shapes(const Pointfs &shape,
|
||||
}
|
||||
is_load_bedtype_textures = false; //reload textures
|
||||
is_load_extruder_only_area_textures = false; // reload textures
|
||||
m_next_bedtype_texture = 0;
|
||||
m_next_extruder_only_area_texture = 0;
|
||||
calc_bounding_boxes();
|
||||
|
||||
update_logo_texture_filename(texture_filename);
|
||||
@@ -7086,53 +7031,120 @@ bool PartPlateList::init_extruder_only_area_info()
|
||||
return true;
|
||||
}
|
||||
|
||||
void PartPlateList::load_bedtype_textures()
|
||||
static GLint logo_texture_size()
|
||||
{
|
||||
if (PartPlateList::is_load_bedtype_textures) return;
|
||||
|
||||
init_bed_type_info();
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048;
|
||||
for (int i = 0; i < (unsigned int)btCount; ++i) {
|
||||
for (int j = 0; j < bed_texture_info[i].parts.size(); j++) {
|
||||
std::string filename = resources_dir() + "/images/" + bed_texture_info[i].parts[j].filename;
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
PartPlateList::bed_texture_info[i].parts[j].texture = new GLTexture();
|
||||
if (!PartPlateList::bed_texture_info[i].parts[j].texture->load_from_svg_file(filename, true, true, true, logo_tex_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename;
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
PartPlateList::is_load_bedtype_textures = true;
|
||||
return std::min<GLint>(OpenGLManager::get_gl_info().get_max_tex_size(), 2048);
|
||||
}
|
||||
|
||||
void PartPlateList::load_extruder_only_area_textures() {
|
||||
if (PartPlateList::is_load_extruder_only_area_textures) return;
|
||||
// Loads the texture of the next untried part across the parts of `infos`, in order, advancing
|
||||
// `next`; false once every part has been tried.
|
||||
static bool load_next_part_texture(PartPlateList::BedTextureInfo* infos, size_t count, size_t& next, bool compress_and_filter)
|
||||
{
|
||||
size_t k = next;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (k >= infos[i].parts.size()) {
|
||||
k -= infos[i].parts.size();
|
||||
continue;
|
||||
}
|
||||
++next;
|
||||
PartPlateList::BedTextureInfo::TexturePart& part = infos[i].parts[k];
|
||||
const std::string filename = resources_dir() + "/images/" + part.filename;
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
part.texture = new GLTexture();
|
||||
if (!part.texture->load_from_svg_file(filename, true, compress_and_filter, compress_and_filter, logo_texture_size()))
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load texture from %1% failed!") % filename;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load texture from %1% failed!") % filename;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ok = init_extruder_only_area_info();
|
||||
if (!ok) {
|
||||
void PartPlateList::load_bedtype_textures()
|
||||
{
|
||||
while (load_next_bedtype_texture()) {}
|
||||
}
|
||||
|
||||
bool PartPlateList::load_next_bedtype_texture()
|
||||
{
|
||||
if (PartPlateList::is_load_bedtype_textures)
|
||||
return false;
|
||||
if (m_next_bedtype_texture == 0)
|
||||
init_bed_type_info();
|
||||
if (load_next_part_texture(bed_texture_info, btCount, m_next_bedtype_texture, true))
|
||||
return true;
|
||||
PartPlateList::is_load_bedtype_textures = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PartPlateList::load_logo_texture()
|
||||
{
|
||||
if (m_logo_texture_filename.empty()) {
|
||||
m_logo_texture.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_logo_texture.get_id() != 0 && m_logo_texture.get_source() == m_logo_texture_filename) {
|
||||
if (m_logo_texture.unsent_compressed_data_available())
|
||||
// sends to gpu the already available compressed levels of the main texture
|
||||
m_logo_texture.send_compressed_data_to_gpu();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_logo_texture.reset();
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (boost::algorithm::iends_with(m_logo_texture_filename, ".svg")) {
|
||||
if (!m_logo_texture.load_from_svg_file(m_logo_texture_filename, true, true, true, logo_texture_size())) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_logo_texture_filename;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (boost::algorithm::iends_with(m_logo_texture_filename, ".png")) {
|
||||
if (!m_logo_texture.load_from_file(m_logo_texture_filename, true, GLTexture::MultiThreaded, true)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % m_logo_texture_filename;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not load logo texture from %1%, unsupported format") % m_logo_texture_filename;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PartPlateList::load_icon_textures()
|
||||
{
|
||||
if (!icon_textures_loaded())
|
||||
generate_icon_textures();
|
||||
}
|
||||
|
||||
bool PartPlateList::load_next_plate_texture()
|
||||
{
|
||||
if (!render_bedtype_logo) {
|
||||
load_logo_texture();
|
||||
return false;
|
||||
}
|
||||
return load_next_bedtype_texture() || load_next_cali_texture() || load_next_extruder_only_area_texture();
|
||||
}
|
||||
|
||||
void PartPlateList::load_extruder_only_area_textures()
|
||||
{
|
||||
while (load_next_extruder_only_area_texture()) {}
|
||||
}
|
||||
|
||||
bool PartPlateList::load_next_extruder_only_area_texture()
|
||||
{
|
||||
if (PartPlateList::is_load_extruder_only_area_textures)
|
||||
return false;
|
||||
if (m_next_extruder_only_area_texture == 0 && !init_extruder_only_area_info()) {
|
||||
PartPlateList::is_load_extruder_only_area_textures = true;
|
||||
return;
|
||||
}
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048;
|
||||
for (int i = 0; i < (unsigned int) ExtruderOnlyAreaType::btAreaCount; ++i) {
|
||||
for (int j = 0; j < extruder_only_area_info[i].parts.size(); j++) {
|
||||
std::string filename = resources_dir() + "/images/" + extruder_only_area_info[i].parts[j].filename;
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
PartPlateList::extruder_only_area_info[i].parts[j].texture = new GLTexture();
|
||||
if (!PartPlateList::extruder_only_area_info[i].parts[j].texture->load_from_svg_file(filename, true, false, false, logo_tex_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename;
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load logo texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (load_next_part_texture(extruder_only_area_info, (size_t) ExtruderOnlyAreaType::btAreaCount, m_next_extruder_only_area_texture, false))
|
||||
return true;
|
||||
PartPlateList::is_load_extruder_only_area_textures = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PartPlateList::init_cali_texture_info()
|
||||
@@ -7147,26 +7159,19 @@ void PartPlateList::init_cali_texture_info()
|
||||
|
||||
void PartPlateList::load_cali_textures()
|
||||
{
|
||||
if (PartPlateList::is_load_cali_texture) return;
|
||||
while (load_next_cali_texture()) {}
|
||||
}
|
||||
|
||||
init_cali_texture_info();
|
||||
GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size();
|
||||
GLint logo_tex_size = (max_tex_size < 2048) ? max_tex_size : 2048;
|
||||
for (int i = 0; i < (unsigned int)btCount; ++i) {
|
||||
for (int j = 0; j < cali_texture_info.parts.size(); j++) {
|
||||
std::string filename = resources_dir() + "/images/" + cali_texture_info.parts[j].filename;
|
||||
if (boost::filesystem::exists(filename)) {
|
||||
PartPlateList::cali_texture_info.parts[j].texture = new GLTexture();
|
||||
if (!PartPlateList::cali_texture_info.parts[j].texture->load_from_svg_file(filename, true, true, true, logo_tex_size)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": load cali texture from %1% failed!") % filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool PartPlateList::load_next_cali_texture()
|
||||
{
|
||||
if (PartPlateList::is_load_cali_texture)
|
||||
return false;
|
||||
if (m_next_cali_texture == 0)
|
||||
init_cali_texture_info();
|
||||
if (load_next_part_texture(&cali_texture_info, 1, m_next_cali_texture, true))
|
||||
return true;
|
||||
PartPlateList::is_load_cali_texture = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PartPlateList::on_extruder_count_changed(int extruder_count)
|
||||
|
||||
@@ -644,6 +644,7 @@ class PartPlateList : public ObjectBase
|
||||
std::string m_hover_tooltip;
|
||||
|
||||
bool m_is_dark = false;
|
||||
bool m_icon_textures_dark = false;
|
||||
|
||||
int m_filament_count = 1;
|
||||
|
||||
@@ -943,12 +944,25 @@ public:
|
||||
bool calc_extruder_only_area(Rect &left_only_rect, Rect &right_only_rect);
|
||||
void init_bed_type_info();
|
||||
bool init_extruder_only_area_info();
|
||||
// Each load_*_textures() loads whatever of its set is not loaded yet; each load_next_*()
|
||||
// loads one texture and returns false once none remain.
|
||||
void load_bedtype_textures();
|
||||
bool load_next_bedtype_texture();
|
||||
void load_extruder_only_area_textures();
|
||||
bool load_next_extruder_only_area_texture();
|
||||
// Starts loading the printer's logo texture, or sends the levels compressed since; false when
|
||||
// there is no logo to draw.
|
||||
bool load_logo_texture();
|
||||
|
||||
void show_cali_texture(bool show = true);
|
||||
void init_cali_texture_info();
|
||||
void load_cali_textures();
|
||||
bool load_next_cali_texture();
|
||||
bool icon_textures_loaded() const { return m_del_texture.get_id() != 0 && m_icon_textures_dark == m_is_dark; }
|
||||
void load_icon_textures();
|
||||
// Loads the next bed-type, calibration or extruder-area texture, or the logo, which rendering
|
||||
// otherwise loads on first use; false once none remain.
|
||||
bool load_next_plate_texture();
|
||||
|
||||
void on_extruder_count_changed(int extruder_count);
|
||||
|
||||
@@ -960,6 +974,13 @@ public:
|
||||
BedTextureInfo bed_texture_info[btCount];
|
||||
BedTextureInfo cali_texture_info;
|
||||
BedTextureInfo extruder_only_area_info[(unsigned char) Slic3r::ExtruderOnlyAreaType::btAreaCount];
|
||||
|
||||
private:
|
||||
// The next part to load in each texture set, counted across the set's parts in order; reset
|
||||
// with the set's is_load_* flag.
|
||||
size_t m_next_bedtype_texture{ 0 };
|
||||
size_t m_next_cali_texture{ 0 };
|
||||
size_t m_next_extruder_only_area_texture{ 0 };
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
@@ -148,7 +148,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _
|
||||
m_line->SetBackgroundColour(wxColor(238, 238, 238));
|
||||
|
||||
m_list_view = new wxScrolledWindow(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(267, -1), wxHSCROLL | wxVSCROLL);
|
||||
m_list_view->SetScrollRate(5, 5);
|
||||
m_list_view->SetScrollRate(5, FromDIP(30));
|
||||
m_list_view->SetMinSize(wxSize(FromDIP(267), FromDIP(378)));
|
||||
m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378)));
|
||||
m_list_view->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
+42
-32
@@ -3056,8 +3056,7 @@ Sidebar::Sidebar(Plater *parent)
|
||||
|
||||
// add filament content
|
||||
p->m_panel_filament_content = new wxScrolledWindow(p->m_filament_area_wrapper, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
p->m_panel_filament_content->SetScrollbars(0, 100, 1, 2);
|
||||
p->m_panel_filament_content->SetScrollRate(0, 5);
|
||||
p->m_panel_filament_content->SetScrollRate(0, FromDIP(20));
|
||||
//p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)});
|
||||
p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255));
|
||||
|
||||
@@ -3149,8 +3148,7 @@ Sidebar::Sidebar(Plater *parent)
|
||||
// 3) Mixed filament rows, in their own scroll area so a long mixed list does not
|
||||
// push the physical filament list off screen.
|
||||
p->m_mixed_scroll_area = new wxScrolledWindow(p->m_filament_area_wrapper, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
p->m_mixed_scroll_area->SetScrollbars(0, 100, 1, 2);
|
||||
p->m_mixed_scroll_area->SetScrollRate(0, 5);
|
||||
p->m_mixed_scroll_area->SetScrollRate(0, FromDIP(20));
|
||||
p->m_mixed_scroll_area->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
||||
{
|
||||
auto* mix_scroll_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -7093,7 +7091,7 @@ struct Plater::priv
|
||||
void remove(size_t obj_idx);
|
||||
bool delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); //BBS
|
||||
void delete_all_objects_from_model();
|
||||
void reset(bool apply_presets_change = false);
|
||||
void reset(bool apply_presets_change = false, bool reload_presets = true);
|
||||
void center_selection();
|
||||
void drop_selection();
|
||||
void mirror(Axis axis);
|
||||
@@ -7951,7 +7949,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
|
||||
if (this->q->get_project_filename().IsEmpty() && this->q->is_empty_project()) {
|
||||
int skip_confirm = e.GetInt();
|
||||
this->q->new_project(skip_confirm, true);
|
||||
// Skips the preset reload; trigger_restore_project()'s callers load the presets first.
|
||||
this->q->new_project(skip_confirm, true, wxString(), false);
|
||||
}
|
||||
});
|
||||
//wxPostEvent(this->q, wxCommandEvent{EVT_RESTORE_PROJECT});
|
||||
@@ -10416,7 +10415,7 @@ void Plater::priv::delete_all_objects_from_model()
|
||||
model.plates_custom_gcodes.clear();
|
||||
}
|
||||
|
||||
void Plater::priv::reset(bool apply_presets_change)
|
||||
void Plater::priv::reset(bool apply_presets_change, bool reload_presets)
|
||||
{
|
||||
// TakeSnapshot below and load_current_presets() further down each re-evaluate the
|
||||
// aggregate dirty flag against a baseline that hasn't been reset yet, so they can toggle
|
||||
@@ -10471,8 +10470,8 @@ void Plater::priv::reset(bool apply_presets_change)
|
||||
// Same reason, one level up: the Design tab keeps the editable document, not the Model, so
|
||||
// clearing the recipe alone leaves the tab showing the previous project's feature tree —
|
||||
// and its next edit syncs that tree straight back into the new project.
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->clear_document();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->clear_document();
|
||||
#endif
|
||||
assemble_view->get_canvas3d()->reset_explosion_ratio();
|
||||
update();
|
||||
@@ -10491,7 +10490,7 @@ void Plater::priv::reset(bool apply_presets_change)
|
||||
wxGetApp().preset_bundle->reset_project_embedded_presets();
|
||||
if (apply_presets_change)
|
||||
wxGetApp().apply_keeped_preset_modifications();
|
||||
else
|
||||
else if (reload_presets)
|
||||
wxGetApp().load_current_presets(false, false);
|
||||
|
||||
//BBS
|
||||
@@ -12861,8 +12860,6 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
notification_manager->set_slicing_progress_export_possible();
|
||||
|
||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||
const std::string lifecycle_job_name = this->background_process.fff_print() ?
|
||||
this->background_process.fff_print()->output_filename() : std::string();
|
||||
this->background_process.reset_export();
|
||||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||
bool has_error = false;
|
||||
@@ -12909,8 +12906,18 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = lifecycle_job_name;
|
||||
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn : (has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||
if (const PrintBase* print = this->background_process.current_print()) {
|
||||
const Model& model = print->model();
|
||||
ctx.id = std::to_string(model.id().id);
|
||||
if (model.model_info)
|
||||
ctx.name = model.model_info->model_name;
|
||||
} else {
|
||||
// Realistically Printbase* print will never be null because select_technology already asserts an active print
|
||||
// and the worker thread asserts it before processing.
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": slicing completed without an active print; lifecycle event has no model ID";
|
||||
}
|
||||
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn :
|
||||
(has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||
ctx.msg = evt.cancelled() ? "cancelled" : (has_error ? lifecycle_error_msg : std::string());
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
||||
}
|
||||
@@ -13243,17 +13250,17 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
|
||||
// Pointer test, not a name lookup: in printer-agents mode this page is TAB_ID_MONITOR_WEB
|
||||
// while the native Device tab holds TAB_ID_MONITOR, and in legacy-web mode it holds
|
||||
// TAB_ID_MONITOR itself.
|
||||
const bool selecting_web_device_tab = main_frame->m_printer_view &&
|
||||
main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view;
|
||||
const bool selecting_web_device_tab = main_frame->m_printer_view_page &&
|
||||
main_frame->m_tabpanel->GetPage(new_sel) == main_frame->m_printer_view_page;
|
||||
if (selecting_web_device_tab) {
|
||||
// Use the selected discovered machine when the preset has no host.
|
||||
main_frame->load_printer_url();
|
||||
} else if (new_name == TAB_ID_MONITOR && wxGetApp().preset_bundle != nullptr) {
|
||||
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
|
||||
if (main_frame->m_printer_view && url.empty()) {
|
||||
if (PrinterWebView* view = PrinterWebView::if_built(); view != nullptr && url.empty()) {
|
||||
// It's missing_connection page, reload so that we can replay the gif image
|
||||
main_frame->m_printer_view->reload();
|
||||
view->reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13984,8 +13991,8 @@ void Plater::priv::unbind_canvas_event_handlers()
|
||||
// The Design tab's viewport is a fourth GLCanvas3D on the same shared GL context, owned by
|
||||
// MainFrame rather than by us — same reach as reset() uses for clear_document(). Null until
|
||||
// the tab has been opened once, so most sessions skip it.
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->unbind_canvas_event_handlers();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->unbind_canvas_event_handlers();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13998,8 +14005,8 @@ void Plater::priv::reset_canvas_volumes()
|
||||
preview->get_canvas3d()->reset_volumes();
|
||||
|
||||
#ifdef SLIC3R_CAD
|
||||
if (wxGetApp().mainframe != nullptr && wxGetApp().mainframe->m_design_panel != nullptr)
|
||||
wxGetApp().mainframe->m_design_panel->reset_canvas_volumes();
|
||||
if (DesignPanel* design = DesignPanel::if_built())
|
||||
design->reset_canvas_volumes();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -15411,7 +15418,7 @@ Print& Plater::fff_print() { return p->fff_print; }
|
||||
const SLAPrint& Plater::sla_print() const { return p->sla_print; }
|
||||
SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||
|
||||
int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_name)
|
||||
int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_name, bool reload_presets)
|
||||
{
|
||||
model().calib_pa_pattern.reset(nullptr);
|
||||
model().plates_custom_gcodes.clear();
|
||||
@@ -15459,7 +15466,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
|
||||
// completes, so hold notifications until then to avoid firing on transient flips.
|
||||
ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(p->dirty_state);
|
||||
|
||||
reset(transfer_preset_changes);
|
||||
reset(transfer_preset_changes, reload_presets);
|
||||
reset_project_dirty_after_save();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
@@ -16469,7 +16476,9 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i
|
||||
_obj->config.set_key_value("internal_solid_infill_line_width", new ConfigOptionFloatOrPercent(nozzle_diameter * 1.2f, false));
|
||||
// ORCA: use the pattern parameter
|
||||
_obj->config.set_key_value("top_surface_pattern", new ConfigOptionEnum<InfillPattern>(pattern));
|
||||
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
|
||||
const auto *top_solid_flow = dynamic_cast<const ConfigOptionFloatsNullable *>(_obj->config.option("top_solid_infill_flow_ratio"));
|
||||
_obj->config.set_key_value("top_solid_infill_flow_ratio",
|
||||
new ConfigOptionFloatsNullable(top_solid_flow ? top_solid_flow->size() : 1, 1.0f));
|
||||
_obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45));
|
||||
_obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135));
|
||||
_obj->config.set_key_value("center_of_surface_pattern", new ConfigOptionEnum<CenterOfSurfacePattern>(CenterOfSurfacePattern::Each_Surface));
|
||||
@@ -18044,7 +18053,7 @@ void Plater::deselect_all() { p->deselect_all(); }
|
||||
void Plater::exit_gizmo() { p->exit_gizmo(); }
|
||||
|
||||
void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
|
||||
void Plater::reset(bool apply_presets_change) { p->reset(apply_presets_change); }
|
||||
void Plater::reset(bool apply_presets_change, bool reload_presets) { p->reset(apply_presets_change, reload_presets); }
|
||||
void Plater::reset_with_confirm()
|
||||
{
|
||||
if (p->model.objects.empty() || MessageDialog(static_cast<wxWindow *>(this), _L("All objects will be removed, continue?"),
|
||||
@@ -20061,7 +20070,7 @@ int Plater::export_config_3mf(int plate_idx, Export3mfProgressFn proFn)
|
||||
void Plater::send_calibration_job_finished(wxCommandEvent & evt)
|
||||
{
|
||||
p->main_frame->request_select_tab(TAB_ID_CALIBRATION);
|
||||
auto calibration_panel = p->main_frame->m_calibration;
|
||||
CalibrationPanel* calibration_panel = CalibrationPanel::ensure();
|
||||
if (calibration_panel) {
|
||||
auto curr_wizard = static_cast<CalibrationWizard*>(calibration_panel->get_tabpanel()->GetPage(evt.GetInt()));
|
||||
wxCommandEvent event(EVT_CALIBRATION_JOB_FINISHED);
|
||||
@@ -20093,8 +20102,8 @@ void Plater::print_job_finished(wxCommandEvent &evt)
|
||||
|
||||
dev->set_selected_machine(evt.GetString().ToStdString());
|
||||
p->main_frame->request_select_tab(TAB_ID_MONITOR);
|
||||
//jump to monitor and select device status panel
|
||||
MonitorPanel* curr_monitor = p->main_frame->m_monitor;
|
||||
// Selects the status page on a built Device tab; one built by the switch starts there.
|
||||
MonitorPanel* curr_monitor = MonitorPanel::if_built();
|
||||
if(curr_monitor)
|
||||
curr_monitor->get_tabpanel()->ChangeSelection(MonitorPanel::PrinterTab::PT_STATUS);
|
||||
}
|
||||
@@ -20834,8 +20843,8 @@ void Plater::update_print_error_info(int code, std::string msg, std::string extr
|
||||
if (p->m_send_to_sdcard_dlg) {
|
||||
p->m_send_to_sdcard_dlg->update_print_error_info(code, msg, extra);
|
||||
}
|
||||
if (p->main_frame->m_calibration)
|
||||
p->main_frame->m_calibration->update_print_error_info(code, msg, extra);
|
||||
if (CalibrationPanel* calibration = CalibrationPanel::if_built())
|
||||
calibration->update_print_error_info(code, msg, extra);
|
||||
}
|
||||
|
||||
wxString Plater::get_project_filename(const wxString& extension) const
|
||||
@@ -21128,7 +21137,8 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar
|
||||
{
|
||||
printer_name.Replace("Bambu Lab", "", false);
|
||||
wxString content;
|
||||
bool device_page = (wxGetApp().mainframe == nullptr) && (wxGetApp().mainframe->m_monitor->IsShown());
|
||||
MainFrame* frame = wxGetApp().mainframe;
|
||||
bool device_page = frame != nullptr && frame->m_monitor_page->in_book();
|
||||
if (type == PrinterWarningType::NOT_CONNECTED) {
|
||||
if (device_page) {
|
||||
content = wxString::Format(_L("Printer not connected. Please go to the device page to connect %s before syncing."),
|
||||
|
||||
@@ -339,7 +339,7 @@ public:
|
||||
// Helper: returns config indices where filament_is_mixed == false
|
||||
std::vector<size_t> physical_filament_config_indices() const;
|
||||
|
||||
int new_project(bool skip_confirm = false, bool silent = false, const wxString& project_name = wxString());
|
||||
int new_project(bool skip_confirm = false, bool silent = false, const wxString& project_name = wxString(), bool reload_presets = true);
|
||||
// BBS: save & backup
|
||||
void load_project(wxString const & filename = "", wxString const & originfile = "-");
|
||||
int save_project(bool saveAs = false);
|
||||
@@ -499,7 +499,7 @@ public:
|
||||
void deselect_all();
|
||||
void exit_gizmo();
|
||||
void remove(size_t obj_idx);
|
||||
void reset(bool apply_presets_change = false);
|
||||
void reset(bool apply_presets_change = false, bool reload_presets = true);
|
||||
void reset_with_confirm();
|
||||
//BBS: return int for various result
|
||||
int close_with_confirm(std::function<bool(bool yes_or_no)> second_check = nullptr); // BBS close project
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Lazy.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Holders to build at idle, ordered by prebuild_order(), run one slice at a time. The
|
||||
// holders outlive the queue. Main thread only.
|
||||
class PrebuildQueue
|
||||
{
|
||||
public:
|
||||
struct Slice
|
||||
{
|
||||
std::string name; // the task that ran; empty when nothing was pending
|
||||
int units{ 0 }; // units run
|
||||
long long ms{ 0 }; // time spent
|
||||
bool completed{ false }; // the task has no more units
|
||||
bool remaining{ false }; // some task is still pending
|
||||
};
|
||||
|
||||
// Lower order runs first, equal order in the order added.
|
||||
void add(LazyBase& task)
|
||||
{
|
||||
auto after = std::find_if(m_tasks.begin(), m_tasks.end(), [&](const LazyBase* t) { return t->prebuild_order() > task.prebuild_order(); });
|
||||
m_tasks.insert(after, &task);
|
||||
}
|
||||
|
||||
void clear() { m_tasks.clear(); }
|
||||
|
||||
bool pending() const
|
||||
{
|
||||
return std::any_of(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); });
|
||||
}
|
||||
|
||||
// The task names in queue order, comma separated.
|
||||
std::string names() const
|
||||
{
|
||||
std::string out;
|
||||
for (const LazyBase* t : m_tasks)
|
||||
out += (out.empty() ? "" : ", ") + t->name();
|
||||
return out;
|
||||
}
|
||||
|
||||
// Runs units of the first pending task until it completes, budget_ms of now_ms() have
|
||||
// passed, or interrupt() is true after a unit; on_unit gets the task name and each
|
||||
// unit's duration. A task with no work is passed over and stays in the queue, so one
|
||||
// whose work returns is pending again.
|
||||
Slice run_slice(int budget_ms,
|
||||
const std::function<long long()>& now_ms,
|
||||
const std::function<bool()>& interrupt,
|
||||
const std::function<void(const std::string&, long long)>& on_unit = {})
|
||||
{
|
||||
Slice slice;
|
||||
auto next = std::find_if(m_tasks.begin(), m_tasks.end(), [](const LazyBase* t) { return t->pending(); });
|
||||
if (next == m_tasks.end())
|
||||
return slice;
|
||||
// The pointer is copied out, since a unit may add tasks and reallocate m_tasks.
|
||||
LazyBase* const task = *next;
|
||||
slice.name = task->name();
|
||||
const long long started = now_ms();
|
||||
for (;;) {
|
||||
const long long unit_started = now_ms();
|
||||
const bool more = task->build_step();
|
||||
const long long now = now_ms();
|
||||
++slice.units;
|
||||
if (on_unit)
|
||||
on_unit(slice.name, now - unit_started);
|
||||
slice.ms = now - started;
|
||||
if (!more) {
|
||||
slice.completed = true;
|
||||
break;
|
||||
}
|
||||
if (slice.ms >= budget_ms || interrupt())
|
||||
break;
|
||||
}
|
||||
slice.remaining = pending();
|
||||
return slice;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<LazyBase*> m_tasks;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -1044,8 +1044,8 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
if (m_bambu_cloud_checkbox) m_bambu_cloud_checkbox->Enable(!enabled);
|
||||
}
|
||||
else if (param == "hide_login_side_panel") {
|
||||
if (wxGetApp().mainframe && wxGetApp().mainframe->m_webview) {
|
||||
wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built()) {
|
||||
home->SendCloudProvidersInfo();
|
||||
}
|
||||
}
|
||||
// ORCA: apply the preview dimming change immediately to the currently loaded preview
|
||||
@@ -1254,9 +1254,8 @@ wxBoxSizer *PreferencesDialog::create_item_bambu_cloud(wxString title, wxString
|
||||
app_config->save();
|
||||
|
||||
// Update homepage visibility immediately
|
||||
auto *mainframe = wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview)
|
||||
mainframe->m_webview->SendCloudProvidersInfo();
|
||||
if (WebViewPanel* home = WebViewPanel::if_built())
|
||||
home->SendCloudProvidersInfo();
|
||||
});
|
||||
|
||||
m_sizer->Add(cb, 0, wxALIGN_CENTER);
|
||||
@@ -1476,7 +1475,7 @@ void PreferencesDialog::create()
|
||||
app_config = get_app_config();
|
||||
|
||||
m_parent = new MyscrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_parent->SetScrollRate(5, 5);
|
||||
m_parent->SetScrollRate(0, FromDIP(20));
|
||||
m_parent->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
m_sizer_body = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
@@ -32,7 +32,7 @@ PrintOptionsDialog::PrintOptionsDialog(wxWindow* parent)
|
||||
|
||||
|
||||
m_scrollwindow = new wxScrolledWindow(this, wxID_ANY);
|
||||
m_scrollwindow->SetScrollRate(0, FromDIP(10));
|
||||
m_scrollwindow->SetScrollRate(0, FromDIP(20));
|
||||
m_scrollwindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord));
|
||||
m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord));
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "wx/textctrl.h"
|
||||
#include <wx/timer.h>
|
||||
#include <memory>
|
||||
#include "Lazy.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -34,7 +35,7 @@ namespace GUI {
|
||||
class PrinterWebViewHandler;
|
||||
|
||||
|
||||
class PrinterWebView : public wxPanel {
|
||||
class PrinterWebView : public wxPanel, public LazyInstance<PrinterWebView> {
|
||||
public:
|
||||
PrinterWebView(wxWindow *parent);
|
||||
virtual ~PrinterWebView();
|
||||
|
||||
@@ -44,6 +44,7 @@ const std::vector<std::string> license_list = {
|
||||
|
||||
ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir()));
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
if (strlang != "")
|
||||
@@ -67,6 +68,7 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
||||
m_auxiliary = new AuxiliaryPanel(this);
|
||||
m_auxiliary->Hide();
|
||||
main_sizer->Add(m_auxiliary, wxSizerFlags().Expand().Proportion(1));
|
||||
add_build_steps_of(*m_auxiliary);
|
||||
Bind(EVT_AUXILIARY_DONE, [this](wxCommandEvent& e) { update_model_data();});
|
||||
|
||||
SetSizer(main_sizer);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Auxiliary.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
#define AUFILE_GREY700 wxColour(107, 107, 107)
|
||||
#define AUFILE_GREY500 wxColour(158, 158, 158)
|
||||
@@ -59,7 +60,7 @@ struct project_file{
|
||||
std::string size;
|
||||
};
|
||||
|
||||
class ProjectPanel : public wxPanel
|
||||
class ProjectPanel : public wxPanel, public StagedBuild, public LazyInstance<ProjectPanel>
|
||||
{
|
||||
private:
|
||||
std::atomic<bool> m_web_init_completed{false};
|
||||
@@ -90,7 +91,6 @@ public:
|
||||
void msw_rescale();
|
||||
void update_model_data();
|
||||
void clear_model_info();
|
||||
void init_auxiliary() { m_auxiliary->init_auxiliary(); }
|
||||
|
||||
bool Show(bool show);
|
||||
void OnScriptMessage(wxWebViewEvent& evt);
|
||||
|
||||
@@ -1225,7 +1225,7 @@ size_t PublishSettingsDialog::category_index_for(
|
||||
}
|
||||
|
||||
category.scroll = new wxScrolledWindow(category.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
category.scroll->SetScrollRate(0, 10);
|
||||
category.scroll->SetScrollRate(0, FromDIP(20));
|
||||
category.scroll->SetBackgroundColour(GetBackgroundColour());
|
||||
category.list_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
category.scroll->SetSizer(category.list_sizer);
|
||||
|
||||
@@ -78,7 +78,7 @@ ReleaseNoteDialog::ReleaseNoteDialog(Plater *plater /*= nullptr*/)
|
||||
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
|
||||
|
||||
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL);
|
||||
m_vebview_release_note->SetScrollRate(5, 5);
|
||||
m_vebview_release_note->SetScrollRate(5, FromDIP(20));
|
||||
m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
|
||||
|
||||
@@ -144,7 +144,7 @@ UpdatePluginDialog::UpdatePluginDialog(wxWindow* parent /*= nullptr*/)
|
||||
operation_tips->SetMaxSize(wxSize(FromDIP(260), -1));
|
||||
|
||||
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_vebview_release_note->SetScrollRate(5, 5);
|
||||
m_vebview_release_note->SetScrollRate(5, FromDIP(20));
|
||||
m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
m_vebview_release_note->SetMinSize(wxSize(FromDIP(260), FromDIP(150)));
|
||||
m_vebview_release_note->SetMaxSize(wxSize(FromDIP(260), FromDIP(150)));
|
||||
@@ -279,7 +279,7 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent)
|
||||
m_simplebook_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
m_scrollwindows_release_note = new wxScrolledWindow(m_simplebook_release_note, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL);
|
||||
m_scrollwindows_release_note->SetScrollRate(5, 5);
|
||||
m_scrollwindows_release_note->SetScrollRate(5, FromDIP(20));
|
||||
m_scrollwindows_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
|
||||
//webview
|
||||
@@ -540,7 +540,7 @@ SecondaryCheckDialog::SecondaryCheckDialog(wxWindow* parent, wxWindowID id, cons
|
||||
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
|
||||
|
||||
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_vebview_release_note->SetScrollRate(0, 5);
|
||||
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
|
||||
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
|
||||
m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380)));
|
||||
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
|
||||
@@ -825,7 +825,7 @@ PrintErrorDialog::PrintErrorDialog(wxWindow* parent, wxWindowID id, const wxStri
|
||||
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(5));
|
||||
|
||||
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_vebview_release_note->SetScrollRate(0, 5);
|
||||
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
|
||||
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
|
||||
m_vebview_release_note->SetMinSize(wxSize(FromDIP(320), FromDIP(250)));
|
||||
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
|
||||
@@ -1124,7 +1124,7 @@ ConfirmBeforeSendDialog::ConfirmBeforeSendDialog(wxWindow* parent, wxWindowID id
|
||||
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
|
||||
|
||||
m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_vebview_release_note->SetScrollRate(0, 5);
|
||||
m_vebview_release_note->SetScrollRate(0, FromDIP(20));
|
||||
m_vebview_release_note->SetBackgroundColour(*wxWHITE);
|
||||
m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380)));
|
||||
m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15));
|
||||
|
||||
@@ -31,7 +31,7 @@ SafetyOptionsDialog::SafetyOptionsDialog(wxWindow* parent)
|
||||
SetSize(FromDIP(480),FromDIP(320));
|
||||
|
||||
m_scrollwindow = new wxScrolledWindow(this, wxID_ANY);
|
||||
m_scrollwindow->SetScrollRate(0, FromDIP(10));
|
||||
m_scrollwindow->SetScrollRate(0, FromDIP(20));
|
||||
m_scrollwindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord));
|
||||
m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord));
|
||||
@@ -272,4 +272,4 @@ void SafetyOptionsDialog::show_idel_heating_toast(const wxString &text)
|
||||
m_idel_heating_toast_timer.StartOnce(3000);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -99,8 +99,11 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr<SceneRaycasterItem> item)
|
||||
}
|
||||
}
|
||||
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera,
|
||||
const ClippingPlane* clipping_plane, SceneRaycaster::EHitMode mode) const
|
||||
{
|
||||
// Orca: Picking may favor an already selected volume for interaction, while camera
|
||||
// navigation must always use the geometrically closest visible scene surface.
|
||||
// helper class used to return currently selected volume as hit when overlapping with other volumes
|
||||
// to allow the user to click and drag on a selected volume
|
||||
class VolumeKeeper
|
||||
@@ -110,7 +113,11 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
bool m_selected_volume_already_found{ false };
|
||||
|
||||
public:
|
||||
VolumeKeeper() {
|
||||
explicit VolumeKeeper(bool enabled) {
|
||||
// Orca: Disable selected-volume bias for navigation raycasts.
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
const Selection& selection = wxGetApp().plater()->get_selection();
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
@@ -135,7 +142,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
}
|
||||
};
|
||||
|
||||
VolumeKeeper volume_keeper;
|
||||
VolumeKeeper volume_keeper(mode == EHitMode::Picking);
|
||||
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) {
|
||||
@@ -154,7 +161,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
|
||||
HitResult ret;
|
||||
|
||||
auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
auto test_raycasters = [this, is_closest, clipping_plane, mode, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type);
|
||||
const Vec3f camera_forward = camera.get_dir_forward().cast<float>();
|
||||
@@ -163,12 +170,22 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
if (!item->is_active())
|
||||
continue;
|
||||
|
||||
// Each plate's component 0 is its surface; the remaining Bed IDs are controls.
|
||||
// Keep controls clickable, but do not use them as camera-pan anchors.
|
||||
if (mode != EHitMode::Picking && type == EType::Bed &&
|
||||
decode_id(type, item->get_id()) % PartPlate::GRABBER_COUNT != 0)
|
||||
continue;
|
||||
|
||||
current_hit.raycaster_id = item->get_id();
|
||||
const Transform3d& trafo = item->get_transform();
|
||||
if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
|
||||
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
|
||||
current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>();
|
||||
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) {
|
||||
// Orca: Perspective rays away from the viewport center are not parallel to camera_forward.
|
||||
// Keep picking's legacy policy, but accept every front-facing navigation surface.
|
||||
const Vec3f view_direction = mode != EHitMode::Picking && camera.get_type() == Camera::EType::Perspective ?
|
||||
Vec3f((current_hit.position.cast<double>() - camera.get_position()).cast<float>()) : camera_forward;
|
||||
if (item->use_back_faces() || current_hit.normal.dot(view_direction) < 0.0f) {
|
||||
if (is_closest(camera, current_hit.position)) {
|
||||
if (volume_keeper.is_active()) {
|
||||
if (volume_keeper.check_hit_result(current_hit))
|
||||
@@ -182,14 +199,18 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
||||
}
|
||||
};
|
||||
|
||||
if (!m_gizmos.empty())
|
||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||
// Orca: Gizmo geometry is an interaction target, not a valid depth anchor for camera movement.
|
||||
if (mode == EHitMode::Picking) {
|
||||
if (!m_gizmos.empty())
|
||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||
|
||||
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||
}
|
||||
|
||||
if (!m_gizmos_on_top || !ret.is_valid()) {
|
||||
if (camera.is_looking_downward() && !m_bed.empty())
|
||||
// Orca: In perspective the bottom of the viewport can see the bed even at a horizontal view.
|
||||
if ((mode == EHitMode::SceneOnly || (mode == EHitMode::Picking && camera.is_looking_downward())) && !m_bed.empty())
|
||||
test_raycasters(EType::Bed, mouse_pos, camera, ret);
|
||||
if (!m_volumes.empty())
|
||||
test_raycasters(EType::Volume, mouse_pos, camera, ret);
|
||||
|
||||
@@ -57,6 +57,15 @@ public:
|
||||
FallbackGizmo = 2000000
|
||||
};
|
||||
|
||||
// Orca: Navigation needs the closest visible scene surface, without picking-specific
|
||||
// gizmo and selected-volume priority.
|
||||
enum class EHitMode : unsigned char
|
||||
{
|
||||
Picking,
|
||||
SceneOnly,
|
||||
VolumesOnly // Navigation when the bed is hidden.
|
||||
};
|
||||
|
||||
struct HitResult
|
||||
{
|
||||
EType type{ EType::None };
|
||||
@@ -97,7 +106,8 @@ public:
|
||||
|
||||
void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; }
|
||||
|
||||
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr,
|
||||
EHitMode mode = EHitMode::Picking) const;
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
void render_hit(const Camera& camera);
|
||||
|
||||
@@ -151,7 +151,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
|
||||
|
||||
wxBoxSizer* m_scroll_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_scroll_area = new wxScrolledWindow(this);
|
||||
m_scroll_area->SetScrollRate(0, 20);
|
||||
m_scroll_area->SetScrollRate(0, FromDIP(20));
|
||||
m_scroll_area->SetBackgroundColour(m_colour_def_color);
|
||||
m_scroll_area->SetMinSize(wxSize(FromDIP(700), FromDIP(600)));
|
||||
m_scroll_area->SetMaxSize(wxSize(FromDIP(700), FromDIP(600)));
|
||||
@@ -710,7 +710,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater)
|
||||
//show bind failed info
|
||||
m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)), wxVSCROLL);
|
||||
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
|
||||
m_sw_print_failed_info->SetScrollRate(0, 5);
|
||||
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
|
||||
m_sw_print_failed_info->SetMinSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)));
|
||||
m_sw_print_failed_info->SetMaxSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)));
|
||||
|
||||
@@ -3468,7 +3468,7 @@ void SelectMachineDialog::navigate_to_timelapse_page()
|
||||
main_frame->jump_to_monitor();
|
||||
|
||||
// then switch to Storage (Media) tab inside Monitor
|
||||
auto* monitor = dynamic_cast<MonitorPanel*>(main_frame->m_monitor);
|
||||
MonitorPanel* monitor = MonitorPanel::if_built();
|
||||
if (monitor) {
|
||||
auto* tabpanel = monitor->get_tabpanel();
|
||||
if (tabpanel) {
|
||||
|
||||
@@ -327,7 +327,7 @@ SelectMachinePopup::SelectMachinePopup(wxWindow *parent)
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL);
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE);
|
||||
m_scrolledWindow->SetScrollRate(0, 5);
|
||||
m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y);
|
||||
auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL);
|
||||
m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow);
|
||||
m_scrolledWindow->Layout();
|
||||
|
||||
@@ -280,7 +280,7 @@ SendMultiMachinePage::SendMultiMachinePage(Plater* plater)
|
||||
|
||||
m_main_scroll = new ScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_main_scroll->SetBackgroundColour(*wxWHITE);
|
||||
m_main_scroll->SetScrollRate(5, 5);
|
||||
m_main_scroll->SetScrollRate(5, FromDIP(20));
|
||||
|
||||
m_sizer_body = new wxBoxSizer(wxVERTICAL);
|
||||
m_main_page = create_page();
|
||||
@@ -1382,7 +1382,7 @@ wxPanel* SendMultiMachinePage::create_page()
|
||||
|
||||
scroll_macine_list = new wxScrolledWindow(main_page, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(800), FromDIP(300)), wxHSCROLL | wxVSCROLL);
|
||||
scroll_macine_list->SetBackgroundColour(*wxWHITE);
|
||||
scroll_macine_list->SetScrollRate(5, 5);
|
||||
scroll_macine_list->SetScrollRate(5, FromDIP(SEND_ITEM_MAX_HEIGHT));
|
||||
scroll_macine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT)));
|
||||
scroll_macine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT)));
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater)
|
||||
//show bind failed info
|
||||
m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL);
|
||||
m_sw_print_failed_info->SetBackgroundColour(*wxWHITE);
|
||||
m_sw_print_failed_info->SetScrollRate(0, 5);
|
||||
m_sw_print_failed_info->SetScrollRate(0, FromDIP(20));
|
||||
m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125)));
|
||||
m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125)));
|
||||
|
||||
|
||||
@@ -147,9 +147,11 @@ constexpr std::array<ShortcutInfo, size_t(Shortcut::Count)> shortcut_table = {{
|
||||
SHORTCUT(Search, "search", L("Search"), GLOBAL, { 'F', CTRL }),
|
||||
SHORTCUT(SwitchView, "switch_view", L("Switch between Prepare/Preview"), CANVAS, { WXK_TAB }),
|
||||
SHORTCUT(CollapseSidebar, "collapse_sidebar", L("Collapse/Expand the sidebar"), CANVAS, { WXK_TAB, SHIFT }),
|
||||
SHORTCUT(SpeedDial, "speed_dial", L("Open the speed dial"), GLOBAL, { WXK_SPACE }),
|
||||
SHORTCUT(ReloadDevicePage, "reload_device_page", L("Reload the device page"), CANVAS, { WXK_F5 }),
|
||||
SHORTCUT(KeyboardShortcuts, "keyboard_shortcuts", L("Show keyboard shortcuts list"), CANVAS, { '?' }),
|
||||
|
||||
// Speed Dial
|
||||
SHORTCUT(SpeedDial, "speed_dial", L("Open the Speed Dial"), GLOBAL, { WXK_SPACE }),
|
||||
}};
|
||||
|
||||
#undef SHORTCUT
|
||||
@@ -177,6 +179,7 @@ constexpr std::array<SectionInfo, size_t(ShortcutSection::Count)> section_table
|
||||
{ Shortcut::ViewDefault, L("Camera") },
|
||||
{ Shortcut::ShowLabels, L("Display") },
|
||||
{ Shortcut::Preferences, L("Application") },
|
||||
{ Shortcut::SpeedDial, L("Speed Dial") },
|
||||
}};
|
||||
|
||||
constexpr bool sections_follow_table_order()
|
||||
|
||||
@@ -47,7 +47,9 @@ enum class Shortcut : uint8_t {
|
||||
// Display
|
||||
ShowLabels, ShowWireframe, ToggleGcodeWindow, ToggleOneLayerMode,
|
||||
// Application
|
||||
Preferences, Search, SwitchView, CollapseSidebar, SpeedDial, ReloadDevicePage, KeyboardShortcuts,
|
||||
Preferences, Search, SwitchView, CollapseSidebar, ReloadDevicePage, KeyboardShortcuts,
|
||||
// Speed Dial
|
||||
SpeedDial,
|
||||
Count
|
||||
};
|
||||
|
||||
@@ -66,7 +68,7 @@ struct ShortcutInfo
|
||||
|
||||
// Headings of the shortcuts dialog, in listing order.
|
||||
enum class ShortcutSection : uint8_t {
|
||||
Project, SlicingAndPrinting, Selection, Editing, Objects, Placement, Gizmos, Sliders, PaintingTools, Camera, Display, Application,
|
||||
Project, SlicingAndPrinting, Selection, Editing, Objects, Placement, Gizmos, Sliders, PaintingTools, Camera, Display, Application, SpeedDial,
|
||||
Count
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/WebViewHostDialog.hpp"
|
||||
|
||||
#include "slic3r/Utils/MacDarkMode.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/dcmemory.h>
|
||||
@@ -82,39 +84,38 @@ nlohmann::json speed_dial_ui_strings()
|
||||
{"shortcut_alt", alt},
|
||||
{"shortcut_ctrl", ctrl},
|
||||
|
||||
{"sd_search", _u8L("Search actions")},
|
||||
{"sd_clear", _u8L("Clear")},
|
||||
{"sd_search_n", _u8L("Search %s actions")},
|
||||
{"sd_recent", _u8L("Recent")},
|
||||
{"sd_plugins", _u8L("Plugins")},
|
||||
{"sd_other", _u8L("Other")},
|
||||
{"sd_no_match_total", _u8L("No actions match (Total: %s)")},
|
||||
{"sd_no_actions", _u8L("No actions yet")},
|
||||
{"sd_no_tabs_match", _u8L("No tabs match")},
|
||||
{"sd_no_tabs", _u8L("No tabs")},
|
||||
{"sd_result_count", _u8L("Showing %s of %s actions")},
|
||||
{"sd_search", _u8L("Search actions")},
|
||||
{"sd_clear", _u8L("Clear")},
|
||||
{"sd_recent", _u8L("Recent")},
|
||||
{"sd_plugins", _u8L("Plugins")},
|
||||
{"sd_other", _u8L("Other")},
|
||||
{"sd_no_match_total", _u8L("No actions match (Total: %s)")},
|
||||
{"sd_no_actions", _u8L("No actions yet")},
|
||||
{"sd_no_tabs_match", _u8L("No tabs match")},
|
||||
{"sd_no_tabs", _u8L("No tabs")},
|
||||
{"sd_result_count", _u8L("Showing %s actions")},
|
||||
{"sd_result_count_all", _u8L("%s actions")},
|
||||
{"sd_tab_count", _u8L("%s tabs")},
|
||||
{"sd_tab_count", _u8L("%s tabs")},
|
||||
{"sd_tab_match_count", _u8L("%s matches")},
|
||||
{"sd_favs_full", _u8L("Favourites are full (%s max)")},
|
||||
{"sd_go_to_pct", _u8L("Go to %s%% of the layer range")},
|
||||
{"sd_enter_pct", _u8L("Enter a layer percentage (0-100)")},
|
||||
{"sd_go_layer_ph", _u8L("Go to layer %% (0-100)")},
|
||||
{"sd_go_tab_ph", _u8L("Go to tab")},
|
||||
{"sd_fav_slot", _u8L("Favourite %s (%s)")},
|
||||
{"sd_pin_fav", _u8L("Pin to favourites (%s)")},
|
||||
{"sd_unpin_fav", _u8L("Unpin from favourites (%s)")},
|
||||
{"sd_remove_fav", _u8L("Remove from favourites")},
|
||||
{"sd_move_left", _u8L("Move left")},
|
||||
{"sd_move_right", _u8L("Move right")},
|
||||
{"sd_unpin", _u8L("Unpin")},
|
||||
{"sd_mode_advanced", _u8L("Advanced")},
|
||||
{"sd_mode_expert", _u8L("Expert")},
|
||||
{"sd_mode_develop", _u8L("Developer")},
|
||||
{"sd_wiki_f1", _u8L("Wiki (F1)")},
|
||||
{"sd_no_wiki", _u8L("No wiki page for this action")},
|
||||
{"sd_show_details", _u8L("Show details")},
|
||||
{"sd_hide_details", _u8L("Hide details")},
|
||||
{"sd_favs_full", _u8L("Favourites are full (%s max)")},
|
||||
{"sd_go_to_pct", _u8L("Go to %s%% of the layer range")},
|
||||
{"sd_enter_pct", _u8L("Enter a layer percentage (0-100)")},
|
||||
{"sd_go_layer_ph", _u8L("Go to layer %% (0-100)")},
|
||||
{"sd_go_tab_ph", _u8L("Go to tab")},
|
||||
{"sd_fav_slot", _u8L("Favourite %s (%s)")},
|
||||
{"sd_pin_fav", _u8L("Pin to favourites (%s)")},
|
||||
{"sd_unpin_fav", _u8L("Unpin from favourites (%s)")},
|
||||
{"sd_remove_fav", _u8L("Remove from favourites")},
|
||||
{"sd_move_left", _u8L("Move left")},
|
||||
{"sd_move_right", _u8L("Move right")},
|
||||
{"sd_unpin", _u8L("Unpin")},
|
||||
{"sd_mode_advanced", _u8L("Advanced")},
|
||||
{"sd_mode_expert", _u8L("Expert")},
|
||||
{"sd_mode_develop", _u8L("Developer")},
|
||||
{"sd_wiki_f1", _u8L("Wiki (F1)")},
|
||||
{"sd_no_wiki", _u8L("No wiki page for this action")},
|
||||
{"sd_show_details", _u8L("Show details")},
|
||||
{"sd_hide_details", _u8L("Hide details")},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,6 +179,7 @@ void SpeedDialWebDialog::request_show()
|
||||
if (IsShown()) {
|
||||
Raise();
|
||||
focus_webview(browser(), m_page_ready);
|
||||
repaint_webview();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,6 +191,7 @@ void SpeedDialWebDialog::request_show()
|
||||
// Grab focus now and again on wxEVT_ACTIVATE; grabbing directly on the WebKit widget is
|
||||
// what makes typing reach the search field immediately on open.
|
||||
focus_webview(browser(), m_page_ready);
|
||||
repaint_webview();
|
||||
}
|
||||
|
||||
void SpeedDialWebDialog::on_script_message(const nlohmann::json& payload)
|
||||
@@ -216,7 +219,7 @@ void SpeedDialWebDialog::handle_web_command(const nlohmann::json& payload)
|
||||
// set_favourite() refuses once the bar hits kFavLimit; tell the page so it can undo the
|
||||
// pin and show a "favourites are full" hint instead of silently losing the favourite.
|
||||
const std::string fav_id = payload.value("id", "");
|
||||
const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false));
|
||||
const bool ok = wxGetApp().action_registry().set_favourite(fav_id, payload.value("fav", false));
|
||||
if (!ok)
|
||||
call_web_handler({{"command", "favourite_full"}, {"limit", (int) ActionRegistry::kFavLimit}, {"id", fav_id}});
|
||||
} else if (command == "reorder_favourites") {
|
||||
@@ -265,14 +268,36 @@ void SpeedDialWebDialog::resize_to_content(int height)
|
||||
Layout();
|
||||
#ifdef __WXOSX__
|
||||
// WKWebView can lag the dialog's new client size; force the viewport to match so the page is
|
||||
// never painted (and clipped by the rounded layer) below the footer.
|
||||
if (wxWebView* wv = browser()) {
|
||||
const wxSize client = GetClientSize();
|
||||
if (wv->GetSize() != client)
|
||||
wv->SetSize(client);
|
||||
}
|
||||
// never painted (and clipped by the rounded layer) below the footer. Unconditional: on a
|
||||
// re-open the size is often unchanged, and skipping the sync leaves the fresh render unpainted.
|
||||
if (wxWebView* wv = browser())
|
||||
wv->SetSize(GetClientSize());
|
||||
#endif
|
||||
apply_rounded_shape();
|
||||
// A re-open re-renders at (usually) the same size, so nothing above may generate damage.
|
||||
// Repaint explicitly so the newly rendered list is shown without needing user input.
|
||||
repaint_webview();
|
||||
}
|
||||
|
||||
void SpeedDialWebDialog::repaint_webview()
|
||||
{
|
||||
wxWebView* wv = browser();
|
||||
if (!wv)
|
||||
return;
|
||||
// Portable invalidate; the platform blocks below reach the widget/layer that actually paints.
|
||||
wv->Refresh();
|
||||
#ifdef __WXOSX__
|
||||
if (void* nb = wv->GetNativeBackend())
|
||||
WKWebView_force_display(nb);
|
||||
wv->Update();
|
||||
#elif defined(__linux__)
|
||||
// WebKitGTK's WebKitWebView owns its own GdkWindow, so invalidating the wxWebView wrapper
|
||||
// (the GtkScrolledWindow) does not redraw it.
|
||||
if (void* nb = wv->GetNativeBackend())
|
||||
gtk_widget_queue_draw((GtkWidget*) nb);
|
||||
#else
|
||||
wv->Update();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Rounded corners: the webview paints an opaque rectangle, so round the whole top-level window.
|
||||
@@ -331,8 +356,8 @@ void SpeedDialWebDialog::run_action(const std::string& id, const std::string& ti
|
||||
return;
|
||||
|
||||
// Only plugin actions get the "Run plugin?" confirm. Built-in commands act immediately.
|
||||
const bool ask = a->kind == AppActionKind::Plugin && reg.should_ask(id);
|
||||
const std::string atitle = a->title();
|
||||
const bool ask = a->kind == AppActionKind::Plugin && reg.should_ask(id);
|
||||
const std::string atitle = a->title();
|
||||
const ConfigOptionMode required = a->required_mode;
|
||||
|
||||
// Settings the current mode hides require a switch first. Ask while the dial is still up; a
|
||||
@@ -341,16 +366,15 @@ void SpeedDialWebDialog::run_action(const std::string& id, const std::string& ti
|
||||
const wxString setting = title.empty() ? from_u8(atitle) : from_u8(title);
|
||||
if (required == comDevelop) {
|
||||
RichMessageDialog dlg(wxGetApp().mainframe,
|
||||
wxString::Format(_L("\"%s\" is a Developer setting. Enable Developer mode to edit it?"),
|
||||
setting),
|
||||
wxString::Format(_L("\"%s\" is a Developer setting. Enable Developer mode to edit it?"), setting),
|
||||
_L("Developer setting"), wxOK | wxCANCEL);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
wxGetApp().enable_developer_mode();
|
||||
} else {
|
||||
RichMessageDialog dlg(wxGetApp().mainframe,
|
||||
wxString::Format(_L("\"%s\" is a %s setting. Switch from %s mode to %s mode to edit it?"),
|
||||
setting, mode_label(required), mode_label(wxGetApp().get_mode()), mode_label(required)),
|
||||
wxString::Format(_L("\"%s\" is a %s setting. Switch from %s mode to %s mode to edit it?"), setting,
|
||||
mode_label(required), mode_label(wxGetApp().get_mode()), mode_label(required)),
|
||||
_L("Switch settings mode"), wxOK | wxCANCEL);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
@@ -28,6 +28,10 @@ private:
|
||||
void send_actions();
|
||||
void search_tabs();
|
||||
void apply_rounded_shape();
|
||||
// Forces the webview to repaint after it is mapped / re-rendered. The popup is transparent and
|
||||
// chrome-less, so a missed frame leaves it blank until input; platform-specific because the
|
||||
// widget that actually paints is not always the wxWebView wrapper.
|
||||
void repaint_webview();
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
bool m_page_ready{false};
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// A panel whose constructor builds the skeleton and queues the rest as steps, run in order
|
||||
// on the main thread, one at a time.
|
||||
class StagedBuild
|
||||
{
|
||||
public:
|
||||
virtual ~StagedBuild() = default;
|
||||
|
||||
bool built() const
|
||||
{
|
||||
return m_next_step == m_steps.size() &&
|
||||
std::all_of(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return child->built(); });
|
||||
}
|
||||
|
||||
// Runs one step and returns true while more remain. Own steps first, then whatever a
|
||||
// child queued after its steps were forwarded.
|
||||
bool build_step()
|
||||
{
|
||||
if (m_next_step < m_steps.size()) {
|
||||
// Copied out, since a step may queue more steps and reallocate m_steps.
|
||||
auto step = std::move(m_steps[m_next_step++]);
|
||||
step();
|
||||
} else if (StagedBuild* child = unfinished_child()) {
|
||||
child->build_step();
|
||||
}
|
||||
return !built();
|
||||
}
|
||||
|
||||
protected:
|
||||
void add_build_step(std::function<void()> step) { m_steps.push_back(std::move(step)); }
|
||||
|
||||
// Queues one step per step the child has now, and keeps the child so that built() waits
|
||||
// for any it queues later.
|
||||
void add_build_steps_of(StagedBuild& child)
|
||||
{
|
||||
m_children.push_back(&child);
|
||||
for (size_t i = child.m_next_step; i < child.m_steps.size(); ++i)
|
||||
add_build_step([&child] { child.build_step(); });
|
||||
}
|
||||
|
||||
private:
|
||||
StagedBuild* unfinished_child() const
|
||||
{
|
||||
auto it = std::find_if(m_children.begin(), m_children.end(), [](const StagedBuild* child) { return !child->built(); });
|
||||
return it == m_children.end() ? nullptr : *it;
|
||||
}
|
||||
|
||||
std::vector<std::function<void()>> m_steps;
|
||||
std::vector<StagedBuild*> m_children;
|
||||
size_t m_next_step{ 0 };
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
+100
-67
@@ -1303,7 +1303,7 @@ void PrintingTaskPanel::set_star_count(int star_count)
|
||||
StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
|
||||
: wxScrolledWindow(parent, id, pos, size, wxHSCROLL | wxVSCROLL)
|
||||
{
|
||||
this->SetScrollRate(25, 25);
|
||||
SetScrollRate(25, FromDIP(20));
|
||||
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev) return;
|
||||
obj = dev->get_selected_machine();
|
||||
@@ -1329,18 +1329,21 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
|
||||
wxBoxSizer *bSizer_left = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto m_monitoring_sizer = create_monitoring_page();
|
||||
bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
// The sizers are nested here so each step only appends to its own.
|
||||
add_build_step([this, bSizer_left] {
|
||||
auto m_monitoring_sizer = create_monitoring_page();
|
||||
bSizer_left->Add(m_monitoring_sizer, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING));
|
||||
m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING));
|
||||
m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0);
|
||||
auto m_panel_separotor1 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
m_panel_separotor1->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
m_panel_separotor1->SetMinSize(wxSize(-1, PAGE_SPACING));
|
||||
m_panel_separotor1->SetMaxSize(wxSize(-1, PAGE_SPACING));
|
||||
m_monitoring_sizer->Add(m_panel_separotor1, 0, wxEXPAND, 0);
|
||||
|
||||
m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT);
|
||||
m_project_task_panel->init_bitmaps();
|
||||
m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0);
|
||||
m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT);
|
||||
m_project_task_panel->init_bitmaps();
|
||||
m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0);
|
||||
});
|
||||
|
||||
// auto m_panel_separotor2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
// m_panel_separotor2->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
@@ -1360,8 +1363,10 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
m_machine_ctrl_panel->SetDoubleBuffered(true);
|
||||
auto m_machine_control = create_machine_control_page(m_machine_ctrl_panel);
|
||||
m_machine_ctrl_panel->SetSizer(m_machine_control);
|
||||
m_machine_ctrl_panel->Layout();
|
||||
m_machine_control->Fit(m_machine_ctrl_panel);
|
||||
add_build_step([this, m_machine_control] {
|
||||
m_machine_ctrl_panel->Layout();
|
||||
m_machine_control->Fit(m_machine_ctrl_panel);
|
||||
});
|
||||
|
||||
bSizer_status_below->Add(m_machine_ctrl_panel, 0, wxALL, 0);
|
||||
|
||||
@@ -1376,8 +1381,11 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
||||
m_panel_separotor_bottom->SetBackgroundColour(STATUS_PANEL_BG);
|
||||
|
||||
bSizer_status->Add(m_panel_separotor_bottom, 0, wxEXPAND | wxALL, 0);
|
||||
this->SetSizerAndFit(bSizer_status);
|
||||
this->Layout();
|
||||
this->SetSizer(bSizer_status);
|
||||
add_build_step([this, bSizer_status] {
|
||||
bSizer_status->SetSizeHints(this);
|
||||
this->Layout();
|
||||
});
|
||||
}
|
||||
|
||||
StatusBasePanel::~StatusBasePanel()
|
||||
@@ -1609,16 +1617,24 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
|
||||
wxBoxSizer *bSizer_control = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto temp_axis_ctrl_sizer = create_temp_axis_group(parent);
|
||||
auto m_filament_load_sizer = create_filament_group(parent);
|
||||
|
||||
// The slot sizers keep each group's place in bSizer_control.
|
||||
wxBoxSizer *temp_axis_slot = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *filament_slot = new wxBoxSizer(wxVERTICAL);
|
||||
/* ams control box or live nozzle-rack panel (rack printers switch between the two) */
|
||||
wxSizer *ams_rack_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT);
|
||||
|
||||
m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT);
|
||||
add_build_step([this, parent, temp_axis_slot, filament_slot] {
|
||||
temp_axis_slot->Add(create_temp_axis_group(parent), 0, wxEXPAND);
|
||||
filament_slot->Add(create_filament_group(parent), 0, wxEXPAND);
|
||||
});
|
||||
add_build_step([this, parent, ams_rack_sizer] {
|
||||
ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT);
|
||||
});
|
||||
add_build_step([this, parent, ams_rack_sizer] {
|
||||
m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent);
|
||||
m_panel_nozzle_rack->Show(false);
|
||||
ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT);
|
||||
});
|
||||
|
||||
m_ams_rack_switch = new SwitchBoard(parent, _L("Filament"), _L("Hotends"), wxSize(FromDIP(126), FromDIP(26)));
|
||||
m_ams_rack_switch->updateState("left");
|
||||
@@ -1626,12 +1642,12 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
m_ams_rack_switch->Bind(wxCUSTOMEVT_SWITCH_POS, &StatusBasePanel::on_ams_rack_switch, this);
|
||||
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(8));
|
||||
bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(temp_axis_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(m_ams_rack_switch, 0, wxALIGN_CENTRE|wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(ams_rack_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6));
|
||||
bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(filament_slot, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8));
|
||||
bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(4));
|
||||
|
||||
bSizer_right->Add(bSizer_control, 1, wxEXPAND | wxALL, 0);
|
||||
@@ -2223,8 +2239,10 @@ void StatusBasePanel::expand_filament_loading(wxMouseEvent& e)
|
||||
m_filament_step->Show(tag_show);
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->Layout();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->get_status_panel()->Layout();
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::show_ams_group(bool show)
|
||||
@@ -2235,7 +2253,8 @@ void StatusBasePanel::show_ams_group(bool show)
|
||||
m_ams_control->Fit();
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->Layout();
|
||||
}
|
||||
|
||||
// On rack printers, don't clobber the rack view when the user has the switch on "Hotends".
|
||||
@@ -2248,7 +2267,8 @@ void StatusBasePanel::show_ams_group(bool show)
|
||||
m_ams_control->Fit();
|
||||
Layout();
|
||||
Fit();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built())
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2272,8 +2292,10 @@ void StatusBasePanel::show_filament_load_group(bool show)
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
wxGetApp().mainframe->m_monitor->get_status_panel()->Layout();
|
||||
wxGetApp().mainframe->m_monitor->Layout();
|
||||
if (MonitorPanel* monitor = MonitorPanel::if_built()) {
|
||||
monitor->get_status_panel()->Layout();
|
||||
monitor->Layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2426,6 +2448,12 @@ void StatusPanel::update_camera_state(MachineObject* obj)
|
||||
|
||||
StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name)
|
||||
: StatusBasePanel(parent, id, pos, size, style)
|
||||
{
|
||||
// Wires the controls the base class builds in steps, so it is the last step.
|
||||
add_build_step([this] { wire_controls(); });
|
||||
}
|
||||
|
||||
void StatusPanel::wire_controls()
|
||||
{
|
||||
init_scaled_buttons();
|
||||
m_buttons.push_back(m_bpButton_z_10);
|
||||
@@ -2537,47 +2565,50 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
|
||||
|
||||
StatusPanel::~StatusPanel()
|
||||
{
|
||||
// Disconnect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this);
|
||||
m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this);
|
||||
// The controls only exist once the last build step has run.
|
||||
if (built()) {
|
||||
// Disconnect Events
|
||||
m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this);
|
||||
m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this);
|
||||
m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
|
||||
m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
|
||||
m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this);
|
||||
m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this);
|
||||
m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this);
|
||||
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_setting_button->Disconnect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_bed->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_bed_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_kill_focus), NULL, this);
|
||||
m_tempCtrl_nozzle_deputy->Disconnect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_nozzle_temp_set_focus), NULL, this);
|
||||
|
||||
m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this);
|
||||
/*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/
|
||||
m_switch_lamp->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this);
|
||||
/*m_switch_nozzle_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/
|
||||
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
//m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);
|
||||
|
||||
m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this);
|
||||
m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this);
|
||||
m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this);
|
||||
m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this);
|
||||
m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this);
|
||||
m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this);
|
||||
m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this);
|
||||
m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this);
|
||||
m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this);
|
||||
m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this);
|
||||
m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this);
|
||||
m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this);
|
||||
m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this);
|
||||
m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this);
|
||||
m_bpButton_z_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this);
|
||||
m_bpButton_z_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this);
|
||||
m_bpButton_z_down_1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_1), NULL, this);
|
||||
m_bpButton_z_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_down_10), NULL, this);
|
||||
m_bpButton_e_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_up_10), NULL, this);
|
||||
m_bpButton_e_down_10->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_e_down_10), NULL, this);
|
||||
m_nozzle_btn_panel->Disconnect(wxCUSTOMEVT_SWITCH_POS, wxCommandEventHandler(StatusPanel::on_nozzle_selected), NULL, this);
|
||||
m_switch_speed->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this);
|
||||
m_calibration_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this);
|
||||
m_options_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_print_options), NULL, this);
|
||||
m_safety_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_safety_options), NULL, this);
|
||||
m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this);
|
||||
}
|
||||
|
||||
// remove warning dialogs
|
||||
if (abort_dlg != nullptr)
|
||||
@@ -5219,7 +5250,9 @@ void StatusPanel::set_default()
|
||||
m_filament_step->Hide();
|
||||
error_info_reset();
|
||||
#ifndef __WXGTK__
|
||||
SetFocus();
|
||||
// Also reached while the panel is built off screen.
|
||||
if (IsShownOnScreen())
|
||||
SetFocus();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "HMS.hpp"
|
||||
#include "PartSkipDialog.hpp"
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
#include "StagedBuild.hpp"
|
||||
|
||||
class StepIndicator;
|
||||
|
||||
@@ -377,7 +378,7 @@ public:
|
||||
void paint(wxPaintEvent&);
|
||||
};
|
||||
|
||||
class StatusBasePanel : public wxScrolledWindow
|
||||
class StatusBasePanel : public wxScrolledWindow, public StagedBuild
|
||||
{
|
||||
protected:
|
||||
wxBitmap m_item_placeholder;
|
||||
@@ -445,7 +446,7 @@ protected:
|
||||
|
||||
|
||||
wxMediaCtrl3 * m_media_ctrl;
|
||||
MediaPlayCtrl * m_media_play_ctrl;
|
||||
MediaPlayCtrl * m_media_play_ctrl{nullptr};
|
||||
|
||||
Label * m_staticText_printing;
|
||||
wxStaticBitmap *m_bitmap_thumbnail;
|
||||
@@ -628,6 +629,7 @@ class StatusPanel : public StatusBasePanel
|
||||
{
|
||||
private:
|
||||
friend class MonitorPanel;
|
||||
void wire_controls();
|
||||
bool load_thumbnail_from_url(const wxString &url, MachineObject *obj);
|
||||
|
||||
protected:
|
||||
@@ -679,7 +681,7 @@ protected:
|
||||
std::map<std::string, std::string> m_print_connect_types;
|
||||
std::vector<Button *> m_buttons;
|
||||
int last_status;
|
||||
ScoreData *m_score_data;
|
||||
ScoreData *m_score_data = nullptr;
|
||||
wxBitmap* calib_bitmap = nullptr;
|
||||
CalibMode m_calib_mode;
|
||||
CalibrationMethod m_calib_method;
|
||||
|
||||
@@ -700,7 +700,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) :
|
||||
//wxBoxSizer *m_scroll_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_scrolledWindow = new wxScrolledWindow(m_show_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_scrolledWindow->SetBackgroundColour(*wxWHITE);
|
||||
m_scrolledWindow->SetScrollRate(0, 20);
|
||||
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
|
||||
m_scrolledWindow->SetMinSize(wxSize(-1, SyncAmsInfoDialogHeightMAX));
|
||||
m_scrolledWindow->SetMaxSize(wxSize(-1, SyncAmsInfoDialogHeightMAX));
|
||||
m_scrolledWindow->EnableScrolling(false,true);
|
||||
|
||||
+76
-18
@@ -804,6 +804,10 @@ void Tab::OnActivate()
|
||||
}
|
||||
#endif
|
||||
|
||||
// The OnActivate() that shows the tab builds the page.
|
||||
if (wxGetApp().mainframe != nullptr && !wxGetApp().mainframe->is_active_and_shown_tab(m_parent))
|
||||
return;
|
||||
|
||||
// BBS: select on first active
|
||||
if (!m_active_page)
|
||||
restore_last_select_item();
|
||||
@@ -2787,7 +2791,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("is_infill_first", "quality_settings_wall_and_surfaces#print-infill-first");
|
||||
optgroup->append_single_option_line("wall_direction", "quality_settings_wall_and_surfaces#wall-loop-direction");
|
||||
optgroup->append_single_option_line("print_flow_ratio", "quality_settings_wall_and_surfaces#surface-flow-ratio");
|
||||
optgroup->append_single_option_line("top_solid_infill_flow_ratio", "quality_settings_wall_and_surfaces#surface-flow-ratio");
|
||||
optgroup->append_single_option_line("top_solid_infill_flow_ratio", "quality_settings_wall_and_surfaces#surface-flow-ratio", 0);
|
||||
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio", "quality_settings_wall_and_surfaces#surface-flow-ratio");
|
||||
optgroup->append_single_option_line("set_other_flow_ratios", "quality_settings_wall_and_surfaces#surface-flow-ratio");
|
||||
optgroup->append_single_option_line("first_layer_flow_ratio", "quality_settings_wall_and_surfaces#surface-flow-ratio");
|
||||
@@ -3065,6 +3069,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("wipe_tower_rib_width", "multimaterial_settings_prime_tower#rib-width");
|
||||
optgroup->append_single_option_line("wipe_tower_fillet_wall", "multimaterial_settings_prime_tower#fillet-wall");
|
||||
optgroup->append_single_option_line("wipe_tower_no_sparse_layers", "multimaterial_settings_prime_tower#no-sparse-layers");
|
||||
optgroup->append_single_option_line("wipe_tower_sparse_layers_combination", "multimaterial_settings_prime_tower#combine-sparse-layers");
|
||||
optgroup->append_single_option_line("single_extruder_multi_material_priming", "multimaterial_settings_prime_tower");
|
||||
|
||||
optgroup = page->new_optgroup(L("Filament for Features"), L"param_filament_for_features");
|
||||
@@ -7159,6 +7164,22 @@ void Tab::restore_last_select_item()
|
||||
m_tabctrl->SelectItem(item);
|
||||
}
|
||||
|
||||
bool Tab::page_build_pending() const
|
||||
{
|
||||
return m_active_page != nullptr && (m_active_page->build_pending() || m_active_page->visibility_pending());
|
||||
}
|
||||
|
||||
bool Tab::page_build_step()
|
||||
{
|
||||
if (m_active_page == nullptr)
|
||||
return false;
|
||||
if (m_active_page->build_pending())
|
||||
m_active_page->build_step(m_mode);
|
||||
else
|
||||
m_active_page->update_visibility(m_mode, true);
|
||||
return page_build_pending();
|
||||
}
|
||||
|
||||
void Tab::update_description_lines()
|
||||
{
|
||||
if (m_active_page && m_active_page->title() == "Dependencies" && m_parent_preset_description_line)
|
||||
@@ -7170,6 +7191,14 @@ void Tab::activate_selected_page(std::function<void()> throw_if_canceled)
|
||||
if (!m_active_page)
|
||||
return;
|
||||
|
||||
#ifdef __WXGTK__
|
||||
// Builds the page off screen, since GTK crashes when it desensitizes a multiline text view
|
||||
// that was built on screen and hidden before its first size allocation.
|
||||
const bool hide_view = m_active_page->build_pending() && m_page_view->IsShown();
|
||||
if (hide_view)
|
||||
m_page_view->Hide();
|
||||
ScopeGuard show_view([this, hide_view] { if (hide_view) m_page_view->Show(); });
|
||||
#endif
|
||||
m_active_page->activate(m_mode, throw_if_canceled);
|
||||
update_changed_ui();
|
||||
update_description_lines();
|
||||
@@ -7375,7 +7404,7 @@ void Tab::OnKeyDown(wxKeyEvent& event)
|
||||
|
||||
void Tab::compare_preset()
|
||||
{
|
||||
wxGetApp().mainframe->diff_dialog.show(m_type);
|
||||
DiffPresetDialog::ensure()->show(m_type);
|
||||
}
|
||||
|
||||
void Tab::transfer_options(const std::string &name_from, const std::string &name_to, std::vector<std::string> options)
|
||||
@@ -7543,8 +7572,10 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach, bool save_to_proje
|
||||
wxGetApp().get_tab(preset_type)->update_tab_ui();
|
||||
}
|
||||
|
||||
// update preset comboboxes in DiffPresetDlg
|
||||
wxGetApp().mainframe->diff_dialog.update_presets(m_type);
|
||||
// show() reloads the presets, so only a visible Compare dialog needs updating.
|
||||
DiffPresetDialog* diff_dialog = DiffPresetDialog::if_built();
|
||||
if (diff_dialog != nullptr && diff_dialog->IsShown())
|
||||
diff_dialog->update_presets(m_type);
|
||||
}
|
||||
|
||||
// Called for a currently selected preset.
|
||||
@@ -8577,6 +8608,8 @@ void Page::update_visibility(ConfigOptionMode mode, bool update_contolls_visibil
|
||||
}
|
||||
|
||||
m_show = ret_val;
|
||||
if (update_contolls_visibility)
|
||||
m_visibility_applied = true;
|
||||
#ifdef __WXMSW__
|
||||
if (!m_show) return;
|
||||
// BBS: fix field control position
|
||||
@@ -8604,20 +8637,8 @@ void Page::activate(ConfigOptionMode mode, std::function<void()> throw_if_cancel
|
||||
#else
|
||||
//m_vsizer->AddSpacer(10);
|
||||
#endif
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
// BBS: no line spliter for first group
|
||||
bool first = true;
|
||||
#endif
|
||||
for (auto group : m_optgroups) {
|
||||
if (!group->activate(throw_if_canceled))
|
||||
continue;
|
||||
m_vsizer->Add(group->sizer, 0, wxEXPAND | (group->is_legend_line() ? (wxLEFT|wxTOP) : wxALL), m_parent->FromDIP(5)); // ORCA use less margin on parameters section
|
||||
group->update_visibility(mode);
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
if (first) group->stb->Hide();
|
||||
first = false;
|
||||
#endif
|
||||
group->reload_config();
|
||||
for (size_t i = 0; i < m_optgroups.size(); ++i) {
|
||||
activate_group(i, mode, throw_if_canceled);
|
||||
throw_if_canceled();
|
||||
}
|
||||
|
||||
@@ -8636,11 +8657,48 @@ void Page::activate(ConfigOptionMode mode, std::function<void()> throw_if_cancel
|
||||
#endif
|
||||
}
|
||||
|
||||
// Builds one option group; false when it already has its controls.
|
||||
bool Page::activate_group(size_t i, ConfigOptionMode mode, std::function<void()> throw_if_canceled)
|
||||
{
|
||||
auto& group = m_optgroups[i];
|
||||
if (!group->activate(throw_if_canceled))
|
||||
return false;
|
||||
m_visibility_applied = false;
|
||||
m_vsizer->Add(group->sizer, 0, wxEXPAND | (group->is_legend_line() ? (wxLEFT|wxTOP) : wxALL), m_parent->FromDIP(5)); // ORCA use less margin on parameters section
|
||||
group->update_visibility(mode);
|
||||
#if HIDE_FIRST_SPLIT_LINE
|
||||
// BBS: no line spliter for first group
|
||||
if (i == 0) group->stb->Hide();
|
||||
#endif
|
||||
group->reload_config();
|
||||
return true;
|
||||
}
|
||||
|
||||
// The first group without controls.
|
||||
size_t Page::next_group_to_build() const
|
||||
{
|
||||
return std::find_if(m_optgroups.begin(), m_optgroups.end(), [](const auto& group) { return !group->is_activated(); }) - m_optgroups.begin();
|
||||
}
|
||||
|
||||
bool Page::build_pending() const
|
||||
{
|
||||
return next_group_to_build() < m_optgroups.size();
|
||||
}
|
||||
|
||||
bool Page::build_step(ConfigOptionMode mode)
|
||||
{
|
||||
const size_t i = next_group_to_build();
|
||||
if (i < m_optgroups.size())
|
||||
activate_group(i, mode, [] {});
|
||||
return build_pending();
|
||||
}
|
||||
|
||||
void Page::clear()
|
||||
{
|
||||
for (auto group : m_optgroups)
|
||||
group->clear();
|
||||
m_page_title = NULL;
|
||||
m_visibility_applied = false;
|
||||
}
|
||||
|
||||
void Page::msw_rescale()
|
||||
|
||||
@@ -71,6 +71,7 @@ class Page: public std::enable_shared_from_this<Page>// : public wxScrolledWindo
|
||||
// BBS: new layout
|
||||
wxStaticText* m_page_title;
|
||||
bool m_show = true;
|
||||
bool m_visibility_applied = false;
|
||||
public:
|
||||
//BBS: GUI refactor
|
||||
Page(wxWindow* parent, const wxString& title, int iconID, wxPanel* tab_owner);
|
||||
@@ -94,6 +95,12 @@ public:
|
||||
void reload_config();
|
||||
void update_visibility(ConfigOptionMode mode, bool update_contolls_visibility);
|
||||
void activate(ConfigOptionMode mode, std::function<void()> throw_if_canceled);
|
||||
// Whether an option group has no controls yet.
|
||||
bool build_pending() const;
|
||||
// Builds the next option group that has no controls yet; true while some remain.
|
||||
bool build_step(ConfigOptionMode mode);
|
||||
// Whether the controls have not been shown or hidden for a mode since they were built.
|
||||
bool visibility_pending() const { return !m_visibility_applied; }
|
||||
void clear();
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
@@ -121,6 +128,8 @@ public:
|
||||
std::map<std::string, std::string> m_opt_id_map;
|
||||
|
||||
protected:
|
||||
size_t next_group_to_build() const;
|
||||
bool activate_group(size_t i, ConfigOptionMode mode, std::function<void()> throw_if_canceled);
|
||||
// Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_item_color;
|
||||
};
|
||||
@@ -437,6 +446,10 @@ public:
|
||||
// BBS: new layout
|
||||
void set_expanded(bool value);
|
||||
void restore_last_select_item();
|
||||
// page_build_pending() says whether the selected page has groups without controls or controls
|
||||
// not yet shown for the mode, and page_build_step() does the next of those.
|
||||
bool page_build_pending() const;
|
||||
bool page_build_step();
|
||||
|
||||
static bool validate_custom_gcode(const wxString& title, const std::string& gcode);
|
||||
bool validate_custom_gcodes();
|
||||
|
||||
@@ -534,7 +534,7 @@ public:
|
||||
|
||||
m_content = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_content->SetBackgroundColour(pop_bg);
|
||||
m_content->SetScrollRate(0, FromDIP(5));
|
||||
m_content->SetScrollRate(0, FromDIP(20));
|
||||
auto* outer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
const int pop_w = std::max(FromDIP(213), popup_width);
|
||||
@@ -2274,7 +2274,7 @@ void TextureImportDialog::build_mapping_panel(wxWindow* parent, wxSizer* sizer)
|
||||
|
||||
m_mapping_scroll = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(-1, FromDIP(300)));
|
||||
m_mapping_scroll->SetScrollRate(0, FromDIP(10));
|
||||
m_mapping_scroll->SetScrollRate(0, FromDIP(20));
|
||||
m_mapping_scroll->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
||||
m_mapping_scroll->Bind(wxEVT_MOUSEWHEEL, &TextureImportDialog::dismiss_filament_popup_on_wheel, this);
|
||||
|
||||
@@ -4335,7 +4335,7 @@ void TextureImportDialog::on_dpi_changed(const wxRect&)
|
||||
|
||||
if (m_mapping_scroll) {
|
||||
m_mapping_scroll->SetMinSize(wxSize(-1, FromDIP(300)));
|
||||
m_mapping_scroll->SetScrollRate(0, FromDIP(10));
|
||||
m_mapping_scroll->SetScrollRate(0, FromDIP(20));
|
||||
}
|
||||
|
||||
if (m_btn_skip) {
|
||||
|
||||
@@ -942,7 +942,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_
|
||||
m_sizer_tab->Add(m_table_top, 1, 0, 0);
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL);
|
||||
m_scrolledWindow->SetScrollRate(0, 5);
|
||||
m_scrolledWindow->SetScrollRate(0, FromDIP(20));
|
||||
m_scrolledWindow->SetBackgroundColour(GREY200);
|
||||
m_sizer_bottom = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer_bottom->Add(m_scrolledWindow, 1, wxEXPAND, 0);
|
||||
@@ -1962,8 +1962,9 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe)
|
||||
|
||||
assert(wxGetApp().preset_bundle);
|
||||
|
||||
m_preset_bundle_left = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
||||
m_preset_bundle_right = std::make_unique<PresetBundle>(*wxGetApp().preset_bundle);
|
||||
// show() copies the app's bundle into both before anything is displayed.
|
||||
m_preset_bundle_left = std::make_unique<PresetBundle>();
|
||||
m_preset_bundle_right = std::make_unique<PresetBundle>();
|
||||
|
||||
// Create UI items
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include "Lazy.hpp"
|
||||
|
||||
class ScalableButton;
|
||||
class wxStaticText;
|
||||
@@ -416,7 +417,7 @@ public:
|
||||
//------------------------------------------
|
||||
// DiffPresetDialog
|
||||
//------------------------------------------
|
||||
class DiffPresetDialog : public DPIDialog
|
||||
class DiffPresetDialog : public DPIDialog, public LazyInstance<DiffPresetDialog>
|
||||
{
|
||||
DiffViewCtrl* m_tree { nullptr };
|
||||
wxBoxSizer* m_presets_sizer { nullptr };
|
||||
|
||||
@@ -130,7 +130,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
|
||||
m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15));
|
||||
|
||||
auto m_scrollwindw_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)),wxVSCROLL);
|
||||
m_scrollwindw_release_note->SetScrollRate(0, 5);
|
||||
m_scrollwindw_release_note->SetScrollRate(0, FromDIP(20));
|
||||
m_scrollwindw_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8));
|
||||
m_scrollwindw_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430)));
|
||||
m_scrollwindw_release_note->SetWindowStyle(wxVSCROLL);
|
||||
|
||||
@@ -1629,7 +1629,7 @@ UpgradePanel::UpgradePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
||||
auto m_main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
|
||||
m_scrolledWindow->SetScrollRate(5, 25);
|
||||
m_scrolledWindow->SetScrollRate(5, FromDIP(20));
|
||||
|
||||
m_machine_list_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
@@ -1159,10 +1159,25 @@ bool GuideFrame::run()
|
||||
}
|
||||
|
||||
int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList, std::string filepath, std::string &sVendor, std::string &sType)
|
||||
{
|
||||
std::unordered_set<std::string> visiting;
|
||||
return GetFilamentInfo(VendorDirectory, pFilaList, filepath, sVendor, sType, visiting);
|
||||
}
|
||||
|
||||
int GuideFrame::GetFilamentInfo(const std::string& VendorDirectory, json& pFilaList,
|
||||
const std::string& filepath, std::string& sVendor,
|
||||
std::string& sType, std::unordered_set<std::string>& visiting)
|
||||
{
|
||||
//GetStardardFilePath(filepath);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " GetFilamentInfo:VendorDirectory - " << VendorDirectory << ", Filepath - "<<filepath;
|
||||
|
||||
// Path-scoped guard: without it an `inherits` cycle would recurse forever.
|
||||
// The repeated file was not inserted, so the cycle hit has nothing to erase.
|
||||
if (!visiting.insert(filepath).second) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherits cycle at " << filepath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Resolve this file's own vendor/type into LOCAL variables, independent of
|
||||
// whatever the caller already accumulated. The cache entry for `filepath`
|
||||
// must reflect only this file's own inherits chain — passing the caller's
|
||||
@@ -1197,41 +1212,30 @@ int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList,
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << filepath << " - Not Contains filament_type";
|
||||
|
||||
if (vendor == "" || type == "") {
|
||||
if (jLocal.contains("inherits")) {
|
||||
std::string FName = jLocal["inherits"];
|
||||
if (jLocal.contains("inherits")) {
|
||||
std::string FName = jLocal["inherits"];
|
||||
|
||||
if (!pFilaList.contains(FName)) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "pFilaList - Not Contains inherits filaments: " << FName;
|
||||
status = -1;
|
||||
} else {
|
||||
std::string FPath = pFilaList[FName]["sub_path"];
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Before Format Inherits Path: VendorDirectory - " << VendorDirectory << ", sub_path - " << FPath;
|
||||
wxString strNewFile = wxString::Format("%s%c%s", wxString(VendorDirectory.c_str(), wxConvUTF8), boost::filesystem::path::preferred_separator, FPath);
|
||||
boost::filesystem::path inherits_path(w2s(strNewFile));
|
||||
if (!boost::filesystem::exists(inherits_path))
|
||||
inherits_path = (boost::filesystem::path(m_OrcaFilaLibPath) / boost::filesystem::path(FPath)).make_preferred();
|
||||
|
||||
if (boost::filesystem::exists(inherits_path)) {
|
||||
// Recurse with this file's own (vendor, type) as the chain accumulator.
|
||||
status = GetFilamentInfo(VendorDirectory, pFilaList, inherits_path.string(), vendor, type);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherits File Not Exist: " << inherits_path;
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
if (!pFilaList.contains(FName)) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "pFilaList - Not Contains inherits filaments: " << FName;
|
||||
status = -1;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << filepath << " - Not Contains inherits";
|
||||
if (type == "") {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "sType is Empty";
|
||||
status = -1;
|
||||
std::string FPath = pFilaList[FName]["sub_path"];
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Before Format Inherits Path: VendorDirectory - " << VendorDirectory << ", sub_path - " << FPath;
|
||||
wxString strNewFile = wxString::Format("%s%c%s", wxString(VendorDirectory.c_str(), wxConvUTF8), boost::filesystem::path::preferred_separator, FPath);
|
||||
boost::filesystem::path inherits_path(w2s(strNewFile));
|
||||
if (!boost::filesystem::exists(inherits_path))
|
||||
inherits_path = (boost::filesystem::path(m_OrcaFilaLibPath) / boost::filesystem::path(FPath)).make_preferred();
|
||||
|
||||
if (boost::filesystem::exists(inherits_path)) {
|
||||
// Traverse the full chain for errors; inherited values only fill local gaps.
|
||||
status = GetFilamentInfo(VendorDirectory, pFilaList, inherits_path.string(), vendor, type, visiting);
|
||||
} else {
|
||||
if (vendor == "")
|
||||
vendor = "Generic";
|
||||
status = 0;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inherits File Not Exist: " << inherits_path;
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << filepath << " - Not Contains inherits";
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
@@ -1250,6 +1254,7 @@ int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList,
|
||||
// Merge this file's resolved values into the caller's out-params (fill empties only).
|
||||
if (sVendor.empty()) sVendor = vendor;
|
||||
if (sType.empty()) sType = type;
|
||||
visiting.erase(filepath);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1803,8 +1808,8 @@ int GuideFrame::LoadProfileFamily(std::string strVendor, std::string strFilePath
|
||||
std::string sT;
|
||||
|
||||
int nRet = GetFilamentInfo(vendor_dir.string(),tFilaList, sub_file, sV, sT);
|
||||
if (nRet != 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",GetFilamentInfo Failed, Vendor:" << sV << ",Type:"<< sT;
|
||||
if (nRet != 0 || sV.empty() || sT.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Load Filament:" << s1 << ",unresolved vendor/type, Vendor:" << sV << ",Type:"<< sT;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -110,6 +111,9 @@ public:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) {}
|
||||
|
||||
private:
|
||||
int GetFilamentInfo(const std::string& VendorDirectory, json& pFilaList, const std::string& filepath,
|
||||
std::string& sVendor, std::string& sType, std::unordered_set<std::string>& visiting);
|
||||
|
||||
GUI_App *m_MainPtr;
|
||||
AppConfig m_appconfig_new;
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||
Bind(wxEVT_CLOSE_WINDOW, &WebViewPanel::OnClose, this);
|
||||
|
||||
m_LoginUpdateTimer = nullptr;
|
||||
update_mode();
|
||||
}
|
||||
|
||||
WebViewPanel::~WebViewPanel()
|
||||
@@ -416,8 +417,7 @@ void WebViewPanel::OnClose(wxCloseEvent& evt)
|
||||
|
||||
void WebViewPanel::OnFreshLoginStatus(wxTimerEvent &event)
|
||||
{
|
||||
auto mainframe = Slic3r::GUI::wxGetApp().mainframe;
|
||||
if (mainframe && mainframe->m_webview == this) {
|
||||
if (WebViewPanel::if_built() == this) {
|
||||
auto* app_config = Slic3r::GUI::wxGetApp().app_config;
|
||||
if (app_config && app_config->get_stealth_mode()) return;
|
||||
Slic3r::GUI::wxGetApp().get_login_info(ORCA_CLOUD_PROVIDER);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <wx/tbarbase.h>
|
||||
#include "wx/textctrl.h"
|
||||
#include <wx/timer.h>
|
||||
#include "Lazy.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -33,7 +34,7 @@ class NetworkAgent;
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class WebViewPanel : public wxPanel
|
||||
class WebViewPanel : public wxPanel, public LazyInstance<WebViewPanel>
|
||||
{
|
||||
public:
|
||||
WebViewPanel(wxWindow *parent);
|
||||
|
||||
@@ -62,7 +62,7 @@ CheckList::CheckList(
|
||||
|
||||
s_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, scroll_style);
|
||||
m_scroll_area->SetScrollRate(0, 10);
|
||||
m_scroll_area->SetScrollRate(0, FromDIP(20));
|
||||
m_scroll_area->SetSizer(s_sizer);
|
||||
m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour());
|
||||
m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this);
|
||||
@@ -223,4 +223,4 @@ void CheckList::ShowMenu(wxMouseEvent &evt)
|
||||
wxPoint screen_pos = src->ClientToScreen(evt.GetPosition());
|
||||
wxPoint local_pos = ScreenToClient(screen_pos);
|
||||
PopupMenu(&m, local_pos);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ bool ProgressDialog::Create(const wxString &title, const wxString &message, int
|
||||
m_sizer_main->Add(m_simplebook, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28));
|
||||
} else {
|
||||
m_msg_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
|
||||
m_msg_scrolledWindow->SetScrollRate(0,5);
|
||||
m_msg_scrolledWindow->SetScrollRate(0, FromDIP(20));
|
||||
wxBoxSizer* m_msg_sizer= new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_msg = new wxStaticText(m_msg_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x, -1), 0);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <wx/webviewarchivehandler.h>
|
||||
#include <wx/webviewfshandler.h>
|
||||
#include <wx/weakref.h>
|
||||
#if wxUSE_WEBVIEW_EDGE
|
||||
#include <wx/msw/webview_edge.h>
|
||||
#elif defined(__WXMAC__)
|
||||
@@ -235,7 +236,9 @@ class FakeWebView : public wxWebView
|
||||
wxDEFINE_EVENT(EVT_WEBVIEW_RECREATED, wxCommandEvent);
|
||||
|
||||
static std::vector<wxWebView*> g_webviews;
|
||||
static std::vector<wxWebView*> g_delay_webviews;
|
||||
// Webviews waiting for their script handler while another one is added; adding it yields, so a
|
||||
// view can be destroyed while it waits.
|
||||
static std::vector<wxWeakRef<wxWebView>> g_delay_webviews;
|
||||
|
||||
class WebViewRef : public wxObjectRefData
|
||||
{
|
||||
@@ -340,8 +343,9 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
|
||||
addScriptMessageHandler(webView);
|
||||
while (!g_delay_webviews.empty()) {
|
||||
auto views = std::move(g_delay_webviews);
|
||||
for (auto wv : views)
|
||||
addScriptMessageHandler(wv);
|
||||
for (const wxWeakRef<wxWebView>& wv : views)
|
||||
if (wv)
|
||||
addScriptMessageHandler(wv.get());
|
||||
}
|
||||
}
|
||||
#ifndef __WIN32__
|
||||
|
||||
@@ -1222,7 +1222,7 @@ ImageTransientPopup::ImageTransientPopup( wxWindow *parent, bool scrolled, wxBit
|
||||
m_panel->SetSize(300, 300);
|
||||
|
||||
// And also actually enable them.
|
||||
m_panel->SetScrollRate(10, 10);
|
||||
m_panel->SetScrollRate(10, FromDIP(20));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user