#include "OrcaFilesPanel.h" #include "DeviceManager.hpp" #include "GUI_App.hpp" #include "GUI.hpp" #include "I18N.hpp" #include "ImageGrid.h" #include "MsgDialog.hpp" #include "Widgets/Button.hpp" #include "Widgets/Label.hpp" #include "Widgets/StaticBox.hpp" #include "Widgets/StateColor.hpp" #include "slic3r/Utils/NetworkAgent.hpp" #include "slic3r/Utils/bambu_networking.hpp" #include #include #include #include namespace Slic3r { namespace GUI { OrcaFilesPanel::OrcaFilesPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) , m_bmp_loading(this, "media_loading", 0) , m_bmp_failed(this, "media_failed", 0) , m_bmp_empty(this, "media_empty", 0) { const wxColour light_background("#EEEEEE"); SetBackgroundColour(wxGetApp().dark_mode() ? StateColor::darkModeColorFor(light_background) : light_background); auto* sizer = new wxBoxSizer(wxVERTICAL); auto* toolbar = new wxBoxSizer(wxHORIZONTAL); toolbar->SetMinSize({-1, 75 * em_unit(this) / 10}); m_root_label = new Label(this, _L("gcodes")); m_root_label->SetFont(Label::Head_16); toolbar->Add(m_root_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 24); m_time_panel = new ::StaticBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); m_time_panel->SetCornerRadius(0); m_button_year = new ::Button(m_time_panel, _L("Year"), "", wxBORDER_NONE); m_button_month = new ::Button(m_time_panel, _L("Month"), "", wxBORDER_NONE); m_button_all = new ::Button(m_time_panel, _L("All Files"), "", wxBORDER_NONE); m_button_year->SetToolTip(_L("Group files by year, recent first.")); m_button_month->SetToolTip(_L("Group files by month, recent first.")); m_button_all->SetToolTip(_L("Show all files, recent first.")); for (auto* button : {m_button_year, m_button_month, m_button_all}) { button->SetBackgroundColor(StateColor( std::make_pair(0xEEEEEE, static_cast(StateColor::Checked)), std::make_pair(*wxLIGHT_GREY, static_cast(StateColor::Hovered)), std::make_pair(*wxWHITE, static_cast(StateColor::Normal)))); button->SetTextColor(StateColor( std::make_pair(0x3B4446, static_cast(StateColor::Checked)), std::make_pair(*wxLIGHT_GREY, static_cast(StateColor::Hovered)), std::make_pair(0xABACAC, static_cast(StateColor::Normal)))); button->SetCanFocus(false); } auto* time_sizer = new wxBoxSizer(wxHORIZONTAL); time_sizer->Add(m_button_year, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 24); time_sizer->Add(m_button_month, 0, wxALIGN_CENTER_VERTICAL); time_sizer->Add(m_button_all, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 24); m_time_panel->SetSizer(time_sizer); toolbar->AddStretchSpacer(1); toolbar->Add(m_time_panel, 0, wxALIGN_CENTER_VERTICAL); toolbar->AddStretchSpacer(1); m_button_refresh = new ::Button(this, _L("Refresh")); m_button_refresh->SetToolTip(_L("Reload file list from printer.")); m_button_refresh->SetFont(Label::Body_12); m_button_refresh->SetCornerRadius(12); m_button_refresh->SetPaddingSize({10, 6}); m_button_refresh->SetCanFocus(false); m_button_refresh->SetBorderWidth(0); toolbar->Add(m_button_refresh, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 24); sizer->Add(toolbar, 0, wxEXPAND); m_image_grid = new ImageGrid(this); // Theme this grid instance without changing the shared Bambu defaults. m_image_grid->SetBackgroundColour(GetBackgroundColour()); m_image_grid->SetSelecting(false); m_image_grid->ShowDownload(false); m_image_grid->SetStatus(m_bmp_failed, _L("Please confirm if the printer is connected.")); sizer->Add(m_image_grid, 1, wxEXPAND); SetSizer(sizer); applyThemeColors(); updateGroupButtons(m_group_mode); auto group_clicked = [this](wxCommandEvent& event) { const FileGridGroup mode = event.GetEventObject() == m_button_year ? FileGridGroup::Year : event.GetEventObject() == m_button_month ? FileGridGroup::Month : FileGridGroup::All; m_image_grid->SetGroupMode(static_cast(mode)); updateGroupButtons(mode); }; m_button_year->Bind(wxEVT_COMMAND_BUTTON_CLICKED, group_clicked); m_button_month->Bind(wxEVT_COMMAND_BUTTON_CLICKED, group_clicked); m_button_all->Bind(wxEVT_COMMAND_BUTTON_CLICKED, group_clicked); m_button_refresh->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent& event) { event.Skip(); if (m_model) m_model->Refresh(); }); m_image_grid->Bind(EVT_ITEM_ACTION, [this](wxCommandEvent& event) { const int action = event.GetInt(); if (!m_model || m_device_id.empty()) return; const size_t index = static_cast(event.GetExtraLong()); if (index >= m_model->GetCount()) return; const FileGridCard& card = m_model->GetFile(index); if (card.id.empty()) return; const std::string path = card.id; const std::string name = card.name; if (action == 0) { MessageDialog dlg(this, wxString::Format(_L("Do you want to delete the file '%s' from printer?"), from_u8(name)), _L("Delete file"), wxYES_NO | wxICON_WARNING); if (dlg.ShowModal() != wxID_YES) return; m_model->DeleteFile(path, [this](bool ok) { if (ok) return; MessageDialog(this, _L("Failed to delete the file from printer."), _L("Delete file"), wxOK | wxICON_ERROR).ShowModal(); }); } else if (action == 1) { // Print is .gcode-only for now; .gcode.3mf support to be added later. MessageDialog dlg(this, wxString::Format(_L("Do you want to print the file '%s' from printer?"), from_u8(name)), _L("Print file"), wxYES_NO | wxICON_WARNING); if (dlg.ShowModal() != wxID_YES) return; NetworkAgent* agent = wxGetApp().getAgent(); if (!agent) return; PrintParams params; params.dev_id = m_device_id; params.dst_file = path; params.connection_type = "lan"; if (agent->start_sdcard_print(params, {}, {}) != BAMBU_NETWORK_SUCCESS) MessageDialog(this, _L("Failed to start the print job."), _L("Print file"), wxOK | wxICON_ERROR).ShowModal(); } }); } OrcaFilesPanel::~OrcaFilesPanel() { if (m_model) m_model->SetStatusHandler({}); if (m_model) m_model->SetGroupModeHandler({}); m_image_grid->SetModel(nullptr); m_model.reset(); } void OrcaFilesPanel::UpdateByObj(MachineObject* obj) { const std::string device_id = obj ? obj->get_dev_id() : std::string(); if (device_id == m_device_id) return; m_device_id = device_id; if (m_model) m_model->SetStatusHandler({}); if (m_model) m_model->SetGroupModeHandler({}); m_image_grid->SetModel(nullptr); m_model.reset(); if (m_device_id.empty()) { m_button_refresh->Enable(false); m_image_grid->SetStatus(m_bmp_failed, _L("Please confirm if the printer is connected.")); return; } m_model = boost::make_shared(m_device_id); m_model->SetStatusHandler([this](RemoteFileGridModel::Status status) { updateStatus(status); }); m_model->SetGroupModeHandler([this](FileGridGroup mode) { updateGroupButtons(mode); }); m_model->SetGroupMode(m_group_mode); m_image_grid->SetModel(m_model); m_image_grid->SetFileType(static_cast(FileGridType::Model), {}); m_button_refresh->Enable(true); m_model->Refresh(); } void OrcaFilesPanel::updateStatus(RemoteFileGridModel::Status status) { switch (status) { case RemoteFileGridModel::Status::Loading: m_image_grid->SetStatus(m_bmp_loading, _L("Loading file list...")); break; case RemoteFileGridModel::Status::Ready: break; case RemoteFileGridModel::Status::Empty: m_image_grid->SetStatus(m_bmp_empty, _L("No files")); break; case RemoteFileGridModel::Status::Failed: m_image_grid->SetStatus(m_bmp_failed, _L("Load failed")); break; } } void OrcaFilesPanel::Rescale() { m_bmp_loading.msw_rescale(); m_bmp_failed.msw_rescale(); m_bmp_empty.msw_rescale(); auto* toolbar = GetSizer()->GetItem(static_cast(0))->GetSizer(); toolbar->SetMinSize({-1, 75 * em_unit(this) / 10}); m_root_label->SetFont(Label::Head_16); m_button_refresh->Rescale(); m_button_year->Rescale(); m_button_month->Rescale(); m_button_all->Rescale(); m_image_grid->Rescale(); } void OrcaFilesPanel::on_sys_color_changed() { applyThemeColors(); Layout(); Refresh(); } void OrcaFilesPanel::applyThemeColors() { const bool dark = wxGetApp().dark_mode(); const wxColour light_background("#EEEEEE"); const wxColour light_text("#262E30"); const wxColour light_teal("#009688"); const wxColour background = dark ? StateColor::darkModeColorFor(light_background) : light_background; SetBackgroundColour(background); m_root_label->SetBackgroundColour(background); m_root_label->SetForegroundColour(dark ? StateColor::darkModeColorFor(light_text) : light_text); m_time_panel->SetBackgroundColour(background); m_button_refresh->SetBackgroundColorNormal(dark ? StateColor::darkModeColorFor(light_teal) : light_teal); m_button_refresh->SetTextColorNormal(*wxWHITE); m_image_grid->SetBackgroundColour(background); m_image_grid->Refresh(); m_button_year->Refresh(); m_button_month->Refresh(); m_button_all->Refresh(); } void OrcaFilesPanel::updateGroupButtons(FileGridGroup mode) { m_group_mode = mode; ::Button* buttons[] = {m_button_all, m_button_month, m_button_year}; const int selected = static_cast(mode); for (int i = 0; i < 3; ++i) { buttons[i]->SetValue(i == selected); buttons[i]->SetFont(i == selected ? Label::Head_14 : Label::Body_14); } } }}