feat: support tab icons

This commit is contained in:
Ian Chua
2026-07-29 19:37:17 +08:00
parent e00906a833
commit 3145f28bb7
15 changed files with 260 additions and 104 deletions

View File

@@ -869,11 +869,11 @@ void AuxiliaryPanel::init_tabpanel()
m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE); m_assembly_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::ASSEMBLY_GUIDE);
m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS); m_others_panel = new AuFolderPanel(m_tabpanel, AuxiliaryFolderType::OTHERS);
m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), "", true); m_tabpanel->AddPage(m_designer_panel, _L("Basic Info"), true);
m_tabpanel->AddPage(m_pictures_panel, _L("Pictures"), "", false); 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_bill_of_materials_panel, _L("Bill of Materials"), false);
m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), "", false); m_tabpanel->AddPage(m_assembly_panel, _L("Assembly Guide"), false);
m_tabpanel->AddPage(m_others_panel, _L("Others"), "", false); m_tabpanel->AddPage(m_others_panel, _L("Others"), false);
} }
wxWindow *AuxiliaryPanel::create_side_tools() wxWindow *AuxiliaryPanel::create_side_tools()

View File

@@ -488,7 +488,6 @@ void CalibrationPanel::init_tabpanel() {
selected = true; selected = true;
m_tabpanel->AddPage(m_cali_panels[i], m_tabpanel->AddPage(m_cali_panels[i],
get_calibration_type_name(m_cali_panels[i]->get_calibration_mode()), get_calibration_type_name(m_cali_panels[i]->get_calibration_mode()),
"",
selected); selected);
} }

View File

@@ -1018,8 +1018,8 @@ void MainFrame::update_layout()
{ {
const int home_idx = m_tabpanel->FindPageByName(TAB_ID_HOME); const int home_idx = m_tabpanel->FindPageByName(TAB_ID_HOME);
const size_t prepare_pos = (home_idx == wxNOT_FOUND) ? 0 : static_cast<size_t>(home_idx) + 1; const size_t prepare_pos = (home_idx == wxNOT_FOUND) ? 0 : static_cast<size_t>(home_idx) + 1;
m_tabpanel->InsertPage(prepare_pos, TAB_ID_PREPARE, m_plater, _L("Prepare"), std::string("tab_3d_active"), std::string("tab_3d_active"), false); m_tabpanel->InsertPage(prepare_pos, m_plater, _L("Prepare"), false, Notebook::PAGE_PREPARE);
m_tabpanel->InsertPage(prepare_pos + 1, TAB_ID_PREVIEW, m_plater, _L("Preview"), std::string("tab_preview_active"), std::string("tab_preview_active"), false); m_tabpanel->InsertPage(prepare_pos + 1, m_plater, _L("Preview"), false, Notebook::PAGE_PREVIEW);
} }
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 0); m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 0);
@@ -1315,7 +1315,7 @@ void MainFrame::init_tabpanel() {
select_tab(TAB_ID_HOME); select_tab(TAB_ID_HOME);
m_webview->load_url(url); m_webview->load_url(url);
}); });
m_tabpanel->AddPage(TAB_ID_HOME, m_webview, "", "tab_home_active", "tab_home_active", false); m_tabpanel->AddPage(m_webview, "", false, Notebook::PAGE_HOME);
m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); m_param_panel = new ParamsPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
} }
@@ -1330,7 +1330,7 @@ void MainFrame::init_tabpanel() {
//BBS add pages //BBS add pages
m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_monitor = new MonitorPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_monitor->SetBackgroundColour(*wxWHITE); m_monitor->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor, _L("Device"), std::string("tab_monitor_active"), std::string("tab_monitor_active"), false); m_tabpanel->AddPage(m_monitor, _L("Device"), false, Notebook::PAGE_MONITOR);
m_printer_view = new PrinterWebView(m_tabpanel); m_printer_view = new PrinterWebView(m_tabpanel);
Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) { Bind(EVT_LOAD_PRINTER_URL, [this](LoadPrinterViewEvent &evt) {
@@ -1345,16 +1345,16 @@ void MainFrame::init_tabpanel() {
m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_multi_machine = new MultiMachinePage(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_multi_machine->SetBackgroundColour(*wxWHITE); m_multi_machine->SetBackgroundColour(*wxWHITE);
// TODO: change the bitmap // TODO: change the bitmap
m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), std::string("tab_multi_active"), std::string("tab_multi_active"), false); m_tabpanel->AddPage(m_multi_machine, _L("Multi-device"), false, Notebook::PAGE_MULTI_DEVICE);
} }
m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_project->SetBackgroundColour(*wxWHITE); m_project->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(TAB_ID_PROJECT, m_project, _L("Project"), std::string("tab_auxiliary_active"), std::string("tab_auxiliary_active"), false); m_tabpanel->AddPage(m_project, _L("Project"), false, Notebook::PAGE_PROJECT);
m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_calibration = new CalibrationPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_calibration->SetBackgroundColour(*wxWHITE); m_calibration->SetBackgroundColour(*wxWHITE);
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), std::string("tab_calibration_active"), std::string("tab_calibration_active"), false); m_tabpanel->AddPage(m_calibration, _L("Calibration"), false, Notebook::PAGE_CALIBRATION);
// Plugin pages are appended after the built-in tabs; their ids are namespaced // 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. // (plugin.<plugin_key>.<name>) so they can't collide with the built-in TAB_ID_* constants.
@@ -1397,7 +1397,7 @@ void MainFrame::show_device(bool bBBLPrinter) {
{ {
const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW); const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW);
const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1; const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1;
m_tabpanel->InsertPage(monitor_pos, TAB_ID_MONITOR, m_monitor, _L("Device"), std::string("tab_monitor_active"), std::string("tab_monitor_active")); m_tabpanel->InsertPage(monitor_pos, m_monitor, _L("Device"), false, Notebook::PAGE_MONITOR);
} }
if (wxGetApp().is_enable_multi_machine()) { if (wxGetApp().is_enable_multi_machine()) {
@@ -1410,8 +1410,7 @@ void MainFrame::show_device(bool bBBLPrinter) {
{ {
const int monitor_idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR); const int monitor_idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR);
const size_t multi_pos = (monitor_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(monitor_idx) + 1; const size_t multi_pos = (monitor_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(monitor_idx) + 1;
m_tabpanel->InsertPage(multi_pos, TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), std::string("tab_multi_active"), m_tabpanel->InsertPage(multi_pos, m_multi_machine, _L("Multi-device"), false, Notebook::PAGE_MULTI_DEVICE);
std::string("tab_multi_active"), false);
} }
} }
if (!m_calibration) { if (!m_calibration) {
@@ -1422,8 +1421,7 @@ void MainFrame::show_device(bool bBBLPrinter) {
// Calibration is always appended last (AddPage), so it lands after whichever of Monitor/Multi-device // Calibration is always appended last (AddPage), so it lands after whichever of Monitor/Multi-device
// actually got inserted above — no longer position-sensitive now that insertion position is computed // actually got inserted above — no longer position-sensitive now that insertion position is computed
// from FindPageByName rather than a fixed TabPosition index. // from FindPageByName rather than a fixed TabPosition index.
m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), std::string("tab_calibration_active"), m_tabpanel->AddPage(m_calibration, _L("Calibration"), false, Notebook::PAGE_CALIBRATION);
std::string("tab_calibration_active"), false);
#ifdef _MSW_DARK_MODE #ifdef _MSW_DARK_MODE
wxGetApp().UpdateDarkUIWin(this); wxGetApp().UpdateDarkUIWin(this);
@@ -1459,8 +1457,7 @@ void MainFrame::show_device(bool bBBLPrinter) {
{ {
const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW); const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW);
const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1; const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1;
m_tabpanel->InsertPage(monitor_pos, TAB_ID_MONITOR, m_printer_view, _L("Device"), std::string("tab_monitor_active"), m_tabpanel->InsertPage(monitor_pos, m_printer_view, _L("Device"), false, Notebook::PAGE_MONITOR);
std::string("tab_monitor_active"));
} }
} }
fit_tab_labels(); // ORCA on printer change fit_tab_labels(); // ORCA on printer change

View File

@@ -186,17 +186,17 @@ void MonitorPanel::init_tabpanel()
//m_status_add_machine_panel = new AddMachinePanel(m_tabpanel); //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);
m_tabpanel->AddPage(m_status_info_panel, _L("Status"), "", true); m_tabpanel->AddPage(m_status_info_panel, _L("Status"), true);
m_media_file_panel = new MediaFilePanel(m_tabpanel); 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("Storage"), false);
//m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), "", false); //m_tabpanel->AddPage(m_media_file_panel, _L("Internal Storage"), false);
m_upgrade_panel = new UpgradePanel(m_tabpanel); m_upgrade_panel = new UpgradePanel(m_tabpanel);
m_tabpanel->AddPage(m_upgrade_panel, _CTX(L_CONTEXT("Update", "Firmware"), "Firmware"), "", false); m_tabpanel->AddPage(m_upgrade_panel, _CTX(L_CONTEXT("Update", "Firmware"), "Firmware"), false);
m_hms_panel = new HMSPanel(m_tabpanel); m_hms_panel = new HMSPanel(m_tabpanel);
m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), "", false); m_tabpanel->AddPage(m_hms_panel, _L("Assistant(HMS)"), false);
std::string network_ver = Slic3r::NetworkAgent::get_version(); std::string network_ver = Slic3r::NetworkAgent::get_version();
if (!network_ver.empty()) { if (!network_ver.empty()) {

View File

@@ -86,9 +86,9 @@ void MultiMachinePage::init_tabpanel()
m_cloud_task_manager = new CloudTaskManagerPage(m_tabpanel); m_cloud_task_manager = new CloudTaskManagerPage(m_tabpanel);
m_machine_manager = new MultiMachineManagerPage(m_tabpanel); m_machine_manager = new MultiMachineManagerPage(m_tabpanel);
m_tabpanel->AddPage(m_machine_manager, _L("Device"), "", true); m_tabpanel->AddPage(m_machine_manager, _L("Device"), true);
m_tabpanel->AddPage(m_local_task_manager, _L("Task Sending"), "", false); m_tabpanel->AddPage(m_local_task_manager, _L("Task Sending"), false);
m_tabpanel->AddPage(m_cloud_task_manager, _L("Task Sent"), "", false); m_tabpanel->AddPage(m_cloud_task_manager, _L("Task Sent"), false);
} }
void MultiMachinePage::init_timer() void MultiMachinePage::init_timer()

View File

@@ -132,7 +132,6 @@ void ButtonsListCtrl::SetSelection(int sel)
StateColor text_color = StateColor( StateColor text_color = StateColor(
std::pair{wxColour(254,254, 254), (int) StateColor::Normal} std::pair{wxColour(254,254, 254), (int) StateColor::Normal}
); );
m_pageButtons[m_selection]->SetSelected(false);
m_pageButtons[m_selection]->SetTextColor(text_color); m_pageButtons[m_selection]->SetTextColor(text_color);
} }
@@ -152,17 +151,20 @@ void ButtonsListCtrl::SetSelection(int sel)
StateColor text_color = StateColor( StateColor text_color = StateColor(
std::pair{wxColour(254, 254, 254), (int) StateColor::Normal} std::pair{wxColour(254, 254, 254), (int) StateColor::Normal}
); );
m_pageButtons[m_selection]->SetSelected(true);
m_pageButtons[m_selection]->SetTextColor(text_color); m_pageButtons[m_selection]->SetTextColor(text_color);
Refresh(); Refresh();
} }
bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /* = false*/, const std::string &bmp_name /* = ""*/, const std::string &inactive_bmp_name) bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /* = false*/, const std::string &bmp_name /* = ""*/, int imageId /* = wxBookCtrlBase::NO_IMAGE */)
{ {
Button * btn = new Button(this, text.empty() ? text : " " + text, bmp_name, wxNO_BORDER); Button * btn = new Button(this, text.empty() ? text : " " + text, bmp_name, wxNO_BORDER);
btn->SetCornerRadius(0); btn->SetCornerRadius(0);
if (bmp_name.empty() && m_imageList != nullptr && imageId != wxBookCtrlBase::NO_IMAGE && imageId >= 0 &&
imageId < m_imageList->GetImageCount())
btn->SetIcon(m_imageList->GetBitmap(imageId));
int em = em_unit(this); int em = em_unit(this);
//BBS set size for button //BBS set size for button
btn->SetMinSize({(text.empty() ? 40 : 136) * em / 10, 36 * em / 10}); btn->SetMinSize({(text.empty() ? 40 : 136) * em / 10, 36 * em / 10});
@@ -175,8 +177,6 @@ bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /*
StateColor text_color = StateColor( StateColor text_color = StateColor(
std::pair{wxColour(254,254, 254), (int) StateColor::Normal}); std::pair{wxColour(254,254, 254), (int) StateColor::Normal});
btn->SetTextColor(text_color); btn->SetTextColor(text_color);
btn->SetInactiveIcon(inactive_bmp_name);
btn->SetSelected(false);
btn->Bind(wxEVT_BUTTON, [this, btn](wxCommandEvent& event) { btn->Bind(wxEVT_BUTTON, [this, btn](wxCommandEvent& event) {
if (auto it = std::find(m_pageButtons.begin(), m_pageButtons.end(), btn); it != m_pageButtons.end()) { if (auto it = std::find(m_pageButtons.begin(), m_pageButtons.end(), btn); it != m_pageButtons.end()) {
auto sel = it - m_pageButtons.begin(); auto sel = it - m_pageButtons.begin();
@@ -232,6 +232,23 @@ bool ButtonsListCtrl::SetPageImage(size_t n, const std::string& bmp_name) const
return true; return true;
} }
bool ButtonsListCtrl::SetPageImage(size_t n, int imageId)
{
if (n >= m_pageButtons.size())
return false;
if (imageId == wxBookCtrlBase::NO_IMAGE) {
m_pageButtons[n]->SetIcon(wxBitmap());
return true;
}
if (m_imageList == nullptr || imageId < 0 || imageId >= m_imageList->GetImageCount())
return false;
m_pageButtons[n]->SetIcon(m_imageList->GetBitmap(imageId));
return true;
}
void ButtonsListCtrl::SetPageText(size_t n, const wxString& strText) void ButtonsListCtrl::SetPageText(size_t n, const wxString& strText)
{ {
Button* btn = m_pageButtons[n]; Button* btn = m_pageButtons[n];

View File

@@ -3,8 +3,11 @@
//#ifdef _WIN32 //#ifdef _WIN32
#include <string>
#include <vector> #include <vector>
#include <string>
#include <wx/bookctrl.h> #include <wx/bookctrl.h>
#include <wx/imaglist.h>
#include <wx/sizer.h> #include <wx/sizer.h>
class ScalableButton; class ScalableButton;
@@ -24,9 +27,11 @@ public:
void SetSelection(int sel); void SetSelection(int sel);
void UpdateMode(); void UpdateMode();
void Rescale(); void Rescale();
bool InsertPage(size_t n, const wxString &text, bool bSelect = false, const std::string &bmp_name = "", const std::string &inactive_bmp_name = ""); bool InsertPage(size_t n, const wxString &text, bool bSelect = false, const std::string &bmp_name = "", int imageId = wxBookCtrlBase::NO_IMAGE);
void RemovePage(size_t n); void RemovePage(size_t n);
bool SetPageImage(size_t n, const std::string& bmp_name) const; bool SetPageImage(size_t n, const std::string& bmp_name) const;
bool SetPageImage(size_t n, int imageId);
void SetImageList(wxImageList* imageList) { m_imageList = imageList; }
void SetPageText(size_t n, const wxString& strText); void SetPageText(size_t n, const wxString& strText);
void SetCompact(size_t n, bool compact); // ORCA void SetCompact(size_t n, bool compact); // ORCA
wxString GetPageText(size_t n) const; wxString GetPageText(size_t n) const;
@@ -41,11 +46,22 @@ private:
int m_btn_margin; int m_btn_margin;
int m_line_margin; int m_line_margin;
std::vector<wxString> m_pageLabels; // ORCA std::vector<wxString> m_pageLabels; // ORCA
wxImageList* m_imageList{nullptr};
}; };
class Notebook : public wxBookCtrlBase class Notebook : public wxBookCtrlBase
{ {
public: public:
// Negative values below wxBookCtrlBase::NO_IMAGE are reserved for the built-in
// tabs. Nonnegative values are wxImageList indices supplied by plugin pages.
static constexpr int PAGE_HOME = -2;
static constexpr int PAGE_PREPARE = -3;
static constexpr int PAGE_PREVIEW = -4;
static constexpr int PAGE_MONITOR = -5;
static constexpr int PAGE_MULTI_DEVICE = -6;
static constexpr int PAGE_PROJECT = -7;
static constexpr int PAGE_CALIBRATION = -8;
Notebook(wxWindow * parent, Notebook(wxWindow * parent,
wxWindowID winid = wxID_ANY, wxWindowID winid = wxID_ANY,
const wxPoint & pos = wxDefaultPosition, const wxPoint & pos = wxDefaultPosition,
@@ -104,7 +120,7 @@ public:
// by this control) and show it immediately. // by this control) and show it immediately.
bool ShowNewPage(wxWindow * page) bool ShowNewPage(wxWindow * page)
{ {
return AddPage(wxString(), page, wxString(), "", ""); return AddPage(page, wxString(), false, NO_IMAGE);
} }
@@ -136,16 +152,10 @@ public:
// Implement base class pure virtual methods. // Implement base class pure virtual methods.
// adds a new page to the control bool AddPage(wxWindow* page, const wxString& text, bool bSelect = false, int imageId = NO_IMAGE) override
bool AddPage(const wxString& id,
wxWindow* page,
const wxString& text,
const std::string& bmp_name,
const std::string& inactive_bmp_name,
bool bSelect = false)
{ {
DoInvalidateBestSize(); DoInvalidateBestSize();
return InsertPage(GetPageCount(), id, page, text, bmp_name, inactive_bmp_name, bSelect); return InsertPage(GetPageCount(), page, text, bSelect, imageId);
} }
// Page management // Page management
@@ -154,12 +164,38 @@ public:
const wxString & text, const wxString & text,
bool bSelect = false, bool bSelect = false,
int imageId = NO_IMAGE) override int imageId = NO_IMAGE) override
{
wxString page_name;
std::string bmp_name;
const bool is_fixed_page = get_fixed_page_info(imageId, page_name, bmp_name);
const int stored_image_id = is_fixed_page ? NO_IMAGE : imageId;
if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect, stored_image_id))
return false;
m_pageNames.insert(m_pageNames.begin() + n, page_name);
m_pageImageIds.insert(m_pageImageIds.begin() + n, stored_image_id);
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name, stored_image_id);
if (!DoSetSelectionAfterInsertion(n, bSelect))
page->Hide();
return true;
}
bool InsertPage(size_t n,
const wxString& id,
wxWindow* page,
const wxString& text,
int imageId,
bool bSelect = false)
{ {
if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect, imageId)) if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect, imageId))
return false; return false;
m_pageNames.insert(m_pageNames.begin() + n, wxString()); m_pageNames.insert(m_pageNames.begin() + n, id);
GetBtnsListCtrl()->InsertPage(n, text, bSelect); m_pageImageIds.insert(m_pageImageIds.begin() + n, imageId);
GetBtnsListCtrl()->InsertPage(n, text, bSelect, "", imageId);
if (!DoSetSelectionAfterInsertion(n, bSelect)) if (!DoSetSelectionAfterInsertion(n, bSelect))
page->Hide(); page->Hide();
@@ -172,14 +208,14 @@ public:
wxWindow * page, wxWindow * page,
const wxString & text, const wxString & text,
const std::string& bmp_name = "", const std::string& bmp_name = "",
const std::string& inactive_bmp_name = "",
bool bSelect = false) bool bSelect = false)
{ {
if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect)) if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect))
return false; return false;
m_pageNames.insert(m_pageNames.begin() + n, id); m_pageNames.insert(m_pageNames.begin() + n, id);
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name, inactive_bmp_name); m_pageImageIds.insert(m_pageImageIds.begin() + n, NO_IMAGE);
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name);
// wxBookCtrlBase::InsertPage() only inserts into the page list and sizes the // wxBookCtrlBase::InsertPage() only inserts into the page list and sizes the
// new page to the current page's rect — it never touches visibility. A freshly // new page to the current page's rect — it never touches visibility. A freshly
@@ -222,8 +258,7 @@ public:
return DoSetSelection(n); return DoSetSelection(n);
} }
// Neither labels nor images are supported but we still store the labels // Labels are stored by the custom button list; page images use the wx image-list IDs below.
// just in case the user code attaches some importance to them.
virtual bool SetPageText(size_t n, const wxString & strText) override virtual bool SetPageText(size_t n, const wxString & strText) override
{ {
wxCHECK_MSG(n < GetPageCount(), false, wxS("Invalid page")); wxCHECK_MSG(n < GetPageCount(), false, wxS("Invalid page"));
@@ -239,14 +274,27 @@ public:
return GetBtnsListCtrl()->GetPageText(n); return GetBtnsListCtrl()->GetPageText(n);
} }
virtual bool SetPageImage(size_t WXUNUSED(n), int WXUNUSED(imageId)) override virtual bool SetPageImage(size_t n, int imageId) override
{ {
return false; if (n >= m_pageImageIds.size())
return false;
if (!GetBtnsListCtrl()->SetPageImage(n, imageId))
return false;
m_pageImageIds[n] = imageId;
return true;
} }
virtual int GetPageImage(size_t WXUNUSED(n)) const override virtual int GetPageImage(size_t n) const override
{ {
return NO_IMAGE; return n < m_pageImageIds.size() ? m_pageImageIds[n] : NO_IMAGE;
}
void SetImageList(wxImageList* imageList)
{
m_imageList = imageList;
GetBtnsListCtrl()->SetImageList(imageList);
} }
bool SetPageImage(size_t n, const std::string& bmp_name) bool SetPageImage(size_t n, const std::string& bmp_name)
@@ -271,6 +319,7 @@ public:
virtual bool DeleteAllPages() override virtual bool DeleteAllPages() override
{ {
m_pageNames.clear(); m_pageNames.clear();
m_pageImageIds.clear();
return wxBookCtrlBase::DeleteAllPages(); return wxBookCtrlBase::DeleteAllPages();
} }
@@ -431,6 +480,7 @@ protected:
if (win) if (win)
{ {
m_pageNames.erase(m_pageNames.begin() + page); m_pageNames.erase(m_pageNames.begin() + page);
m_pageImageIds.erase(m_pageImageIds.begin() + page);
GetBtnsListCtrl()->RemovePage(page); GetBtnsListCtrl()->RemovePage(page);
DoSetSelectionAfterRemoval(page); DoSetSelectionAfterRemoval(page);
} }
@@ -454,9 +504,47 @@ protected:
} }
private: private:
static bool get_fixed_page_info(int imageId, wxString& page_name, std::string& bmp_name)
{
switch (imageId) {
case PAGE_HOME:
page_name = wxS("home");
bmp_name = "tab_home_active";
return true;
case PAGE_PREPARE:
page_name = wxS("prepare");
bmp_name = "tab_3d_active";
return true;
case PAGE_PREVIEW:
page_name = wxS("preview");
bmp_name = "tab_preview_active";
return true;
case PAGE_MONITOR:
page_name = wxS("monitor");
bmp_name = "tab_monitor_active";
return true;
case PAGE_MULTI_DEVICE:
page_name = wxS("multi_device");
bmp_name = "tab_multi_active";
return true;
case PAGE_PROJECT:
page_name = wxS("project");
bmp_name = "tab_auxiliary_active";
return true;
case PAGE_CALIBRATION:
page_name = wxS("calibration");
bmp_name = "tab_calibration_active";
return true;
default:
return false;
}
}
void Init(); void Init();
std::vector<wxString> m_pageNames; // index-parallel to wxBookCtrlBase::m_pages std::vector<wxString> m_pageNames; // index-parallel to wxBookCtrlBase::m_pages
std::vector<int> m_pageImageIds; // index-parallel to wxBookCtrlBase::m_pages
wxImageList* m_imageList{nullptr};
wxShowEffect m_showEffect, wxShowEffect m_showEffect,
m_hideEffect; m_hideEffect;

View File

@@ -108,7 +108,7 @@ public:
// by this control) and show it immediately. // by this control) and show it immediately.
bool ShowNewPage(wxWindow * page) bool ShowNewPage(wxWindow * page)
{ {
return AddPage(page, wxString(), ""/*true *//* select it */); return AddPage(page, wxString());
} }
// Set effect to use for showing/hiding pages. // Set effect to use for showing/hiding pages.
@@ -139,14 +139,13 @@ public:
// Implement base class pure virtual methods. // Implement base class pure virtual methods.
// adds a new page to the control
bool AddPage(wxWindow* page, bool AddPage(wxWindow* page,
const wxString& text, const wxString& text,
const std::string& bmp_name, bool bSelect = false,
bool bSelect = false) int imageId = NO_IMAGE) override
{ {
DoInvalidateBestSize(); DoInvalidateBestSize();
return InsertNewPage(GetPageCount(), page, text, bmp_name, bSelect); return InsertPage(GetPageCount(), page, text, bSelect, imageId);
} }
//// Page management //// Page management
@@ -167,23 +166,6 @@ public:
return true; return true;
} }
bool InsertNewPage(size_t n,
wxWindow * page,
const wxString & text,
const std::string& bmp_name = "",
bool bSelect = false)
{
if (!wxBookCtrlBase::InsertPage(n, page, text, bSelect))
return false;
GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name);
if (bSelect)
SetSelection(n);
return true;
}
bool RemovePage(size_t n) bool RemovePage(size_t n)
{ {
if (!wxBookCtrlBase::RemovePage(n)) if (!wxBookCtrlBase::RemovePage(n))

View File

@@ -95,14 +95,11 @@ void Button::SetIcon(const wxString& icon)
} }
} }
void Button::SetInactiveIcon(const wxString &icon) void Button::SetIcon(const wxBitmap& icon)
{ {
if (!icon.IsEmpty()) { this->active_icon = ScalableBitmap();
// BBS set button icon default size to 20 this->active_icon.bmp() = icon;
this->inactive_icon = ScalableBitmap(this, icon.ToStdString(), this->active_icon.px_cnt()); messureSize();
} else {
this->inactive_icon = ScalableBitmap();
}
Refresh(); Refresh();
} }
@@ -260,9 +257,6 @@ void Button::Rescale()
if (this->active_icon.bmp().IsOk()) if (this->active_icon.bmp().IsOk())
this->active_icon.msw_rescale(); this->active_icon.msw_rescale();
if (this->inactive_icon.bmp().IsOk())
this->inactive_icon.msw_rescale();
messureSize(); messureSize();
if(m_has_style) if(m_has_style)
@@ -293,11 +287,7 @@ void Button::render(wxDC& dc)
wxSize szIcon; wxSize szIcon;
wxSize textSize = this->textSize.GetSize(); wxSize textSize = this->textSize.GetSize();
ScalableBitmap icon; ScalableBitmap icon = active_icon;
if (m_selected || ((states & (int)StateColor::State::Hovered) != 0))
icon = active_icon;
else
icon = inactive_icon;
wxSize padding = this->paddingSize; wxSize padding = this->paddingSize;
int spacing = 5; int spacing = 5;
// Wrap text // Wrap text

View File

@@ -34,12 +34,10 @@ class Button : public StaticBox
wxSize minSize; // set by outer wxSize minSize; // set by outer
wxSize paddingSize; wxSize paddingSize;
ScalableBitmap active_icon; ScalableBitmap active_icon;
ScalableBitmap inactive_icon;
StateColor text_color; StateColor text_color;
bool pressedDown = false; bool pressedDown = false;
bool m_selected = true;
bool canFocus = true; bool canFocus = true;
bool isCenter = true; bool isCenter = true;
bool vertical = false; bool vertical = false;
@@ -61,8 +59,7 @@ public:
bool SetFont(const wxFont& font) override; bool SetFont(const wxFont& font) override;
void SetIcon(const wxString& icon); void SetIcon(const wxString& icon);
void SetIcon(const wxBitmap& icon);
void SetInactiveIcon(const wxString& icon);
void SetMinSize(const wxSize& size) override; void SetMinSize(const wxSize& size) override;
void SetMaxSize(const wxSize& size) override; void SetMaxSize(const wxSize& size) override;
@@ -75,8 +72,6 @@ public:
void SetTextColorNormal(wxColor const &color); void SetTextColorNormal(wxColor const &color);
void SetSelected(bool selected = true) { m_selected = selected; }
bool Enable(bool enable = true) override; bool Enable(bool enable = true) override;
void EnableTooltipEvenDisabled();// The tip will be shown even if the button is disabled void EnableTooltipEvenDisabled();// The tip will be shown even if the button is disabled

View File

@@ -14,6 +14,8 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <stdexcept>
#include <wx/bookctrl.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <utility> #include <utility>
@@ -204,6 +206,11 @@ void PluginPages::initialize(Notebook* parent)
if (m_parent == nullptr) if (m_parent == nullptr)
return; return;
// Keep image-list indices stable for the lifetime of this notebook. Removing an image
// would shift every later index, so deregistration only removes the page.
m_image_list = std::make_unique<wxImageList>(20, 20, true, 0);
m_parent->SetImageList(m_image_list.get());
for (const auto& capability : PluginManager::instance().get_plugin_capabilities("", PluginCapabilityType::Pages)) { for (const auto& capability : PluginManager::instance().get_plugin_capabilities("", PluginCapabilityType::Pages)) {
if (capability) if (capability)
on_cap_register(capability->identity()); on_cap_register(capability->identity());
@@ -214,6 +221,9 @@ void PluginPages::shutdown()
{ {
while (!m_pages.empty()) while (!m_pages.empty())
remove_page(m_pages.begin()->first); remove_page(m_pages.begin()->first);
if (m_parent != nullptr)
m_parent->SetImageList(nullptr);
m_image_list.reset();
m_parent = nullptr; m_parent = nullptr;
} }
@@ -235,6 +245,15 @@ void PluginPages::on_cap_register(const PluginCapabilityId& id)
if (!capability) if (!capability)
return; return;
std::string icon;
try {
icon = capability->get_icon();
} catch (const std::exception& error) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to get icon for plugin " << id.plugin_key << ": " << error.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to get icon for plugin " << id.plugin_key;
}
auto* page = new PluginPage(m_parent, std::move(capability)); auto* page = new PluginPage(m_parent, std::move(capability));
if (!page->is_valid()) { if (!page->is_valid()) {
page->Destroy(); page->Destroy();
@@ -242,8 +261,29 @@ void PluginPages::on_cap_register(const PluginCapabilityId& id)
} }
const wxString title = wxString::FromUTF8(id.name); const wxString title = wxString::FromUTF8(id.name);
const wxString page_id = wxString::FromUTF8("plugin." + id.plugin_key + "." + id.name);
if (!m_parent->AddPage(page_id, page, title, "tab_auxiliary_active", "tab_auxiliary_active", false)) { int image_id = wxBookCtrlBase::NO_IMAGE;
if (!icon.empty() && m_image_list) {
try {
boost::filesystem::path icon_path(icon);
const std::string extension = icon_path.extension().string();
if (extension == ".svg" || extension == ".png")
icon_path.replace_extension();
const wxBitmap bitmap = create_scaled_bitmap(icon_path.string(), m_parent, 20);
if (bitmap.IsOk())
image_id = m_image_list->Add(bitmap);
} catch (const std::exception& error) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key;
}
}
page->set_icon_image_id(image_id);
if (!m_parent->AddPage(page, title, false, image_id)) {
if (image_id != wxBookCtrlBase::NO_IMAGE && m_image_list && image_id == m_image_list->GetImageCount() - 1)
m_image_list->Remove(image_id);
page->Destroy(); page->Destroy();
return; return;
} }
@@ -285,12 +325,39 @@ void PluginPages::remove_page(const PluginCapabilityId& id)
return; return;
PluginPage* page = it->second; PluginPage* page = it->second;
const int removed_image_id = page->get_icon_image_id();
page->detach_capability(); page->detach_capability();
if (m_parent != nullptr) { if (m_parent != nullptr) {
const int index = m_parent->FindPage(page); const int index = m_parent->FindPage(page);
if (index != wxNOT_FOUND) if (index != wxNOT_FOUND)
m_parent->RemovePage(static_cast<size_t>(index)); m_parent->RemovePage(static_cast<size_t>(index));
} }
if (m_image_list && removed_image_id != wxBookCtrlBase::NO_IMAGE &&
removed_image_id >= 0 && removed_image_id < m_image_list->GetImageCount()) {
m_image_list->Remove(removed_image_id);
// wxImageList IDs are positional. Removing one shifts all later images down by
// one, so update both the page state and the notebook button for those pages.
for (const auto& [other_id, other_page] : m_pages) {
if (other_id == id)
continue;
const int other_image_id = other_page->get_icon_image_id();
if (other_image_id <= removed_image_id)
continue;
const int updated_image_id = other_image_id - 1;
other_page->set_icon_image_id(updated_image_id);
if (m_parent != nullptr) {
const int other_index = m_parent->FindPage(other_page);
if (other_index != wxNOT_FOUND)
m_parent->SetPageImage(static_cast<size_t>(other_index), updated_image_id);
}
}
}
page->Destroy(); page->Destroy();
m_pages.erase(it); m_pages.erase(it);
} }

View File

@@ -8,6 +8,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <wx/bookctrl.h>
#include <wx/imaglist.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/webview.h> #include <wx/webview.h>
@@ -29,16 +31,20 @@ public:
void on_new_window(wxWebViewEvent& event); void on_new_window(wxWebViewEvent& event);
void on_script_message(wxWebViewEvent& event); void on_script_message(wxWebViewEvent& event);
void push_message(const std::string& message); void push_message(const std::string& message);
void set_icon_image_id(int id) { m_icon_image_id = id; }
int get_icon_image_id() const { return m_icon_image_id; }
private: private:
void load_plugin_content(); void load_plugin_content();
wxString bootstrap_url() const; wxString bootstrap_url() const;
wxString web_base_url() const; wxString web_base_url() const;
wxWebView* m_browser{nullptr}; wxWebView* m_browser{nullptr};
std::shared_ptr<PagesPluginCapability> m_cap; std::shared_ptr<PagesPluginCapability> m_cap;
std::shared_ptr<std::atomic<PluginPage*>> m_lifetime; std::shared_ptr<std::atomic<PluginPage*>> m_lifetime;
bool m_content_loaded{false}; bool m_content_loaded{false};
int m_icon_image_id = wxBookCtrlBase::NO_IMAGE;
}; };
class PluginPages class PluginPages
@@ -47,7 +53,7 @@ public:
PluginPages() = default; PluginPages() = default;
~PluginPages(); ~PluginPages();
PluginPages(const PluginPages&) = delete; PluginPages(const PluginPages&) = delete;
PluginPages& operator=(const PluginPages&) = delete; PluginPages& operator=(const PluginPages&) = delete;
void initialize(Notebook* parent); void initialize(Notebook* parent);
@@ -63,7 +69,9 @@ private:
void remove_page(const PluginCapabilityId& id); void remove_page(const PluginCapabilityId& id);
std::map<PluginCapabilityId, PluginPage*> m_pages; std::map<PluginCapabilityId, PluginPage*> m_pages;
Notebook* m_parent{nullptr}; Notebook* m_parent{nullptr};
std::unique_ptr<wxImageList> m_image_list;
}; };
} // namespace Slic3r } // namespace Slic3r

View File

@@ -23,6 +23,7 @@ void PagesPluginCapability::RegisterBindings(pybind11::module_& module)
.def(py::init<>()) .def(py::init<>())
.def("get_type", &PagesPluginCapability::get_type) .def("get_type", &PagesPluginCapability::get_type)
.def("get_ui", &PagesPluginCapability::get_ui) .def("get_ui", &PagesPluginCapability::get_ui)
.def("get_icon", &PagesPluginCapability::get_icon)
.def("on_message", &PagesPluginCapability::on_message) .def("on_message", &PagesPluginCapability::on_message)
.def( .def(
"post_message", "post_message",

View File

@@ -18,6 +18,7 @@ public:
virtual std::string get_ui() = 0; virtual std::string get_ui() = 0;
virtual void on_message(std::string message) { (void) message; } virtual void on_message(std::string message) { (void) message; }
virtual std::string get_icon() { return {}; }
void post_message(std::string message); void post_message(std::string message);
void set_message_sender(std::function<void(const std::string&)> sender); void set_message_sender(std::function<void(const std::string&)> sender);

View File

@@ -13,6 +13,17 @@ class PyPagesPluginCapabilityTrampoline : public PyPluginCommonTrampoline<PagesP
public: public:
using PyPluginCommonTrampoline<PagesPluginCapability>::PyPluginCommonTrampoline; using PyPluginCommonTrampoline<PagesPluginCapability>::PyPluginCommonTrampoline;
std::string get_icon() override
{
ORCA_PY_OVERRIDE_AUDITED(
::Slic3r::PluginAuditManager::AuditMode::Loading,
[] {},
PYBIND11_OVERRIDE,
std::string,
PagesPluginCapability,
get_icon);
}
std::string get_ui() override std::string get_ui() override
{ {
ORCA_PY_OVERRIDE_AUDITED( ORCA_PY_OVERRIDE_AUDITED(