Micro-refactor

This commit is contained in:
SoftFever
2026-08-19 14:08:19 +08:00
parent 7c55b07736
commit 1e87d56482
10 changed files with 181 additions and 335 deletions
+3 -20
View File
@@ -280,19 +280,8 @@ void AppConfig::set_defaults()
set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap)); set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap));
} }
if (get(SETTING_PLUGIN_PAGES_VISIBLE_COUNT).empty()) // The getter already defaults, parses and clamps; write back what it resolves to.
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT)); set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(get_plugin_pages_visible_count()));
else {
int visible_count = PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
try {
visible_count = std::stoi(get(SETTING_PLUGIN_PAGES_VISIBLE_COUNT));
}
catch (...) {
visible_count = PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
}
visible_count = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(visible_count));
}
if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty()) if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty())
set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false); set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false);
@@ -1657,13 +1646,7 @@ int AppConfig::get_plugin_pages_visible_count() const
catch (...) { catch (...) {
return PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT; return PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
} }
return std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MAX)); return std::clamp(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MIN, PLUGIN_PAGES_VISIBLE_COUNT_MAX);
}
void AppConfig::set_plugin_pages_visible_count(int count)
{
count = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(count));
} }
std::vector<std::string> AppConfig::get_skipped_network_versions() const std::vector<std::string> AppConfig::get_skipped_network_versions() const
-1
View File
@@ -382,7 +382,6 @@ public:
// Number of plugin pages shown as fixed tabs before the rest are collapsed into a // Number of plugin pages shown as fixed tabs before the rest are collapsed into a
// dropdown on the last tab. // dropdown on the last tab.
int get_plugin_pages_visible_count() const; int get_plugin_pages_visible_count() const;
void set_plugin_pages_visible_count(int count);
std::vector<std::string> get_skipped_network_versions() const; std::vector<std::string> get_skipped_network_versions() const;
void add_skipped_network_version(const std::string& version); void add_skipped_network_version(const std::string& version);
+63 -85
View File
@@ -722,7 +722,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'S') { if (can_save_as()) m_plater->save_project(true); return;} if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'S') { if (can_save_as()) m_plater->save_project(true); return;}
else if (evt.CmdDown() && evt.GetKeyCode() == 'S') { if (can_save()) m_plater->save_project(); return;} else if (evt.CmdDown() && evt.GetKeyCode() == 'S') { if (can_save()) m_plater->save_project(); return;}
if (evt.CmdDown() && evt.GetKeyCode() == 'F') { if (evt.CmdDown() && evt.GetKeyCode() == 'F') {
if (m_plater && (m_tabpanel->GetSelectedPageName() == TAB_ID_PREPARE || m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW)) { if (m_plater && is_prepare_or_preview_tab()) {
m_plater->sidebar().can_search(); m_plater->sidebar().can_search();
} }
} }
@@ -1015,12 +1015,12 @@ void MainFrame::update_layout()
case ESettingsLayout::Old: case ESettingsLayout::Old:
{ {
m_plater->Reparent(m_tabpanel); m_plater->Reparent(m_tabpanel);
{ // Right after Home — or first, when there is no Home tab (PositionAfter() would
const int home_idx = m_tabpanel->FindPageByName(TAB_ID_HOME); // append instead, and by now the other built-in tabs are already in place).
const size_t prepare_pos = (home_idx == wxNOT_FOUND) ? 0 : static_cast<size_t>(home_idx) + 1; const int home_idx = m_tabpanel->FindPageByName(TAB_ID_HOME);
m_tabpanel->InsertPage(prepare_pos, m_plater, _L("Prepare"), false, Notebook::PAGE_PREPARE); const size_t prepare_pos = (home_idx == wxNOT_FOUND) ? 0 : static_cast<size_t>(home_idx) + 1;
m_tabpanel->InsertPage(prepare_pos + 1, m_plater, _L("Preview"), false, Notebook::PAGE_PREVIEW); m_tabpanel->InsertPage(prepare_pos, TAB_ID_PREPARE, m_plater, _L("Prepare"), "tab_3d_active");
} m_tabpanel->InsertPage(prepare_pos + 1, TAB_ID_PREVIEW, m_plater, _L("Preview"), "tab_preview_active");
m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 0); m_main_sizer->Add(m_tabpanel, 1, wxEXPAND | wxTOP, 0);
m_tabpanel->Bind(wxCUSTOMEVT_NOTEBOOK_SEL_CHANGED, [this](wxCommandEvent& evt) m_tabpanel->Bind(wxCUSTOMEVT_NOTEBOOK_SEL_CHANGED, [this](wxCommandEvent& evt)
@@ -1290,24 +1290,6 @@ void MainFrame::init_tabpanel() {
if (panel) if (panel)
panel->SetFocus(); panel->SetFocus();
/*switch (sel) {
case TabPosition::tpHome:
show_option(false);
break;
case TabPosition::tp3DEditor:
show_option(true);
break;
case TabPosition::tpPreview:
show_option(true);
break;
case TabPosition::tpMonitor:
show_option(false);
break;
default:
show_option(false);
break;
}*/
}); });
if (wxGetApp().is_editor()) { if (wxGetApp().is_editor()) {
@@ -1317,7 +1299,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(m_webview, "", false, Notebook::PAGE_HOME); m_tabpanel->AddPage(TAB_ID_HOME, m_webview, "", "tab_home_active");
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);
} }
@@ -1332,7 +1314,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(m_monitor, _L("Device"), false, Notebook::PAGE_MONITOR); m_tabpanel->AddPage(TAB_ID_MONITOR, m_monitor, _L("Device"), "tab_monitor_active");
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) {
@@ -1347,16 +1329,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(m_multi_machine, _L("Multi-device"), false, Notebook::PAGE_MULTI_DEVICE); m_tabpanel->AddPage(TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
} }
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(m_project, _L("Project"), false, Notebook::PAGE_PROJECT); m_tabpanel->AddPage(TAB_ID_PROJECT, m_project, _L("Project"), "tab_auxiliary_active");
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(m_calibration, _L("Calibration"), false, Notebook::PAGE_CALIBRATION); m_tabpanel->AddPage(TAB_ID_CALIBRATION, m_calibration, _L("Calibration"), "tab_calibration_active");
// 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.
@@ -1382,9 +1364,14 @@ void MainFrame::show_device(bool should_use_native) {
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents"); const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
// The web page is appended when printer agents are enabled. Remove that // The web Device page is the extra tab printer-agents mode shows alongside the native one.
// extra page before switching back to the normal native/Web layout. // Printers that drive the native Bambu device tab have nothing to put in it, so they don't
if (!use_printer_agents) { // get it — otherwise a Bambu user sees two Device tabs, one of them permanently empty.
const bool want_web_device_tab = use_printer_agents && wxGetApp().preset_bundle != nullptr &&
!wxGetApp().preset_bundle->use_bbl_device_tab();
// 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) { if ((idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR_WEB)) != wxNOT_FOUND) {
m_printer_view->Show(false); m_printer_view->Show(false);
m_tabpanel->RemovePage(idx); m_tabpanel->RemovePage(idx);
@@ -1403,10 +1390,8 @@ void MainFrame::show_device(bool should_use_native) {
m_tabpanel->RemovePage(idx); m_tabpanel->RemovePage(idx);
} }
m_monitor->Show(false); m_monitor->Show(false);
const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW); m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
const size_t monitor_pos = _L("Device"), "tab_monitor_active");
(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"), "tab_monitor_active", false);
} }
if (m_printer_view == nullptr) { if (m_printer_view == nullptr) {
@@ -1427,30 +1412,31 @@ void MainFrame::show_device(bool should_use_native) {
// TODO: change the bitmap // TODO: change the bitmap
if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) { if (m_tabpanel->FindPage(m_multi_machine) == wxNOT_FOUND) {
m_multi_machine->Show(false); m_multi_machine->Show(false);
const int monitor_idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR); // Past the web Device tab when it is already there, so enabling multi-machine
const size_t multi_pos = // later can't wedge this page between the two Device tabs.
(monitor_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(monitor_idx) + 1; m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR_WEB, TAB_ID_MONITOR}),
m_tabpanel->InsertPage(multi_pos, TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), TAB_ID_MULTI_DEVICE, m_multi_machine, _L("Multi-device"), "tab_multi_active");
"tab_multi_active", false);
} }
} }
if (!m_calibration) { if (!m_calibration) {
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);
} }
// Calibration is always the last page, so don't use InsertPage here. Otherwise, if multi_machine page is not enabled,
// the calibration tab won't be properly added as well, due to the TabPosition::tpCalibration no longer matches the real tab position.
if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) { if (m_tabpanel->FindPage(m_calibration) == wxNOT_FOUND) {
m_calibration->Show(false); m_calibration->Show(false);
m_tabpanel->AddPage(m_calibration, _L("Calibration"), false, Notebook::PAGE_CALIBRATION); m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
_L("Calibration"), "tab_calibration_active");
} }
if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) { if (want_web_device_tab) {
m_printer_view->Show(false); if ((idx = m_tabpanel->FindPage(m_printer_view)) == wxNOT_FOUND) {
m_tabpanel->InsertPage(m_tabpanel->GetPageCount(), TAB_ID_MONITOR_WEB, m_printer_view, m_printer_view->Show(false);
_L("Device (Web)"), "tab_monitor_active", false); // Immediately right of the native Device tab, not at the end of the tab bar.
} else { m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MONITOR_WEB,
m_tabpanel->SetPageText(idx, _L("Device (Web)")); m_printer_view, _L("Device (Web)"), "tab_monitor_active");
} else {
m_tabpanel->SetPageText(idx, _L("Device (Web)"));
}
} }
#ifdef _MSW_DARK_MODE #ifdef _MSW_DARK_MODE
@@ -1458,7 +1444,7 @@ void MainFrame::show_device(bool should_use_native) {
#endif // _MSW_DARK_MODE #endif // _MSW_DARK_MODE
fit_tab_labels(); // ORCA on printer change fit_tab_labels(); // ORCA on printer change
m_plugin_pages.relayout(); // keep plugin tabs after the native tabs just mutated above m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
return; return;
} }
@@ -1480,11 +1466,8 @@ void MainFrame::show_device(bool should_use_native) {
m_monitor->SetBackgroundColour(*wxWHITE); m_monitor->SetBackgroundColour(*wxWHITE);
} }
m_monitor->Show(false); m_monitor->Show(false);
{ m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_monitor,
const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW); _L("Device"), "tab_monitor_active");
const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1;
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()) {
if (!m_multi_machine) { if (!m_multi_machine) {
@@ -1493,21 +1476,18 @@ void MainFrame::show_device(bool should_use_native) {
} }
// TODO: change the bitmap // TODO: change the bitmap
m_multi_machine->Show(false); m_multi_machine->Show(false);
{ m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_MONITOR}), TAB_ID_MULTI_DEVICE, m_multi_machine,
const int monitor_idx = m_tabpanel->FindPageByName(TAB_ID_MONITOR); _L("Multi-device"), "tab_multi_active");
const size_t multi_pos = (monitor_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(monitor_idx) + 1;
m_tabpanel->InsertPage(multi_pos, m_multi_machine, _L("Multi-device"), false, Notebook::PAGE_MULTI_DEVICE);
}
} }
if (!m_calibration) { if (!m_calibration) {
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_calibration->Show(false); m_calibration->Show(false);
// Calibration is always appended last (AddPage), so it lands after whichever of Monitor/Multi-device // Last of the built-in tabs, but plugin tabs already sit past it — anchor rather than
// actually got inserted above — no longer position-sensitive now that insertion position is computed // append, so its position doesn't depend on the relayout() below running afterwards.
// from FindPageByName rather than a fixed TabPosition index. m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PROJECT}), TAB_ID_CALIBRATION, m_calibration,
m_tabpanel->AddPage(m_calibration, _L("Calibration"), false, Notebook::PAGE_CALIBRATION); _L("Calibration"), "tab_calibration_active");
#ifdef _MSW_DARK_MODE #ifdef _MSW_DARK_MODE
wxGetApp().UpdateDarkUIWin(this); wxGetApp().UpdateDarkUIWin(this);
@@ -1540,14 +1520,17 @@ void MainFrame::show_device(bool should_use_native) {
}); });
} }
m_printer_view->Show(false); m_printer_view->Show(false);
{ m_tabpanel->InsertPage(m_tabpanel->PositionAfter({TAB_ID_PREVIEW}), TAB_ID_MONITOR, m_printer_view,
const int preview_idx = m_tabpanel->FindPageByName(TAB_ID_PREVIEW); _L("Device"), "tab_monitor_active");
const size_t monitor_pos = (preview_idx == wxNOT_FOUND) ? m_tabpanel->GetPageCount() : static_cast<size_t>(preview_idx) + 1;
m_tabpanel->InsertPage(monitor_pos, m_printer_view, _L("Device"), false, Notebook::PAGE_MONITOR);
}
} }
fit_tab_labels(); // ORCA on printer change fit_tab_labels(); // ORCA on printer change
m_plugin_pages.relayout(); // keep plugin tabs after the native tabs just mutated above m_plugin_pages.relayout(); // re-sync plugin tabs against the native tabs just mutated above
}
bool MainFrame::is_prepare_or_preview_tab() const
{
const wxString tab = m_tabpanel->GetSelectedPageName();
return tab == TAB_ID_PREPARE || tab == TAB_ID_PREVIEW;
} }
void MainFrame::fit_tab_labels() void MainFrame::fit_tab_labels()
@@ -3168,7 +3151,7 @@ void MainFrame::init_menubar_as_editor()
wxGetApp().app_config->set_bool("auto_perspective", !wxGetApp().app_config->get_bool("auto_perspective")); wxGetApp().app_config->set_bool("auto_perspective", !wxGetApp().app_config->get_bool("auto_perspective"));
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
}, },
this, [this]() { return m_tabpanel->GetSelectedPageName() == TAB_ID_PREPARE || m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW; }, this, [this]() { return is_prepare_or_preview_tab(); },
[this]() { return wxGetApp().app_config->get_bool("auto_perspective"); }, this); [this]() { return wxGetApp().app_config->get_bool("auto_perspective"); }, this);
viewMenu->AppendSeparator(); viewMenu->AppendSeparator();
@@ -3186,7 +3169,7 @@ void MainFrame::init_menubar_as_editor()
wxGetApp().toggle_show_3d_navigator(); wxGetApp().toggle_show_3d_navigator();
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
}, },
this, [this]() { return m_tabpanel->GetSelectedPageName() == TAB_ID_PREPARE || m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW; }, this, [this]() { return is_prepare_or_preview_tab(); },
[this]() { return wxGetApp().show_3d_navigator(); }, this); [this]() { return wxGetApp().show_3d_navigator(); }, this);
append_menu_check_item(viewMenu, wxID_ANY, _L("Show Gridlines"), _L("Show Gridlines on plate"), append_menu_check_item(viewMenu, wxID_ANY, _L("Show Gridlines"), _L("Show Gridlines on plate"),
@@ -3194,15 +3177,14 @@ void MainFrame::init_menubar_as_editor()
wxGetApp().toggle_show_plate_gridlines(); wxGetApp().toggle_show_plate_gridlines();
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
}, this, }, this,
[this]() { return m_tabpanel->GetSelectedPageName() == TAB_ID_PREPARE || m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW; }, [this]() { return is_prepare_or_preview_tab(); },
[this]() { return wxGetApp().show_plate_gridlines(); }, this); [this]() { return wxGetApp().show_plate_gridlines(); }, this);
append_menu_item( append_menu_item(
viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"), viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"),
[this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this, [this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this,
[this]() { [this]() {
return (m_tabpanel->GetSelectedPageName() == TAB_ID_PREPARE || m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW) && return is_prepare_or_preview_tab() && m_plater->is_sidebar_enabled();
m_plater->is_sidebar_enabled();
}, },
this); this);
@@ -4025,10 +4007,8 @@ void MainFrame::select_tab(wxPanel* panel)
wxGetApp().params_dialog()->Popup(); wxGetApp().params_dialog()->Popup();
return; return;
} }
// page_name cannot be resolved via panel->GetName() — Prepare and Preview // Not panel->GetName(): Prepare and Preview share the single m_plater window, so the
// share the single m_plater window, so the window itself has no single correct // window has no one correct name. The slot -> id lookup is the only correct resolution.
// name (see Global Constraints). Resolve via Notebook's per-slot m_pageNames
// instead, via the index -> id lookup, which works for any page (built-in or not).
int page_idx = m_tabpanel->FindPage(panel); int page_idx = m_tabpanel->FindPage(panel);
wxString page_name = (page_idx == wxNOT_FOUND) ? wxString() : m_tabpanel->GetPageName(static_cast<size_t>(page_idx)); wxString page_name = (page_idx == wxNOT_FOUND) ? wxString() : m_tabpanel->GetPageName(static_cast<size_t>(page_idx));
if (page_name == TAB_ID_PREPARE && m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW) if (page_name == TAB_ID_PREPARE && m_tabpanel->GetSelectedPageName() == TAB_ID_PREVIEW)
@@ -4082,10 +4062,8 @@ void MainFrame::select_tab(const wxString& id/* = wxString()*/)
m_plater->get_current_canvas3D()->render(); m_plater->get_current_canvas3D()->render();
}*/ }*/
#endif #endif
// NOTE: this checks the ORIGINAL parameter (id), not the resolved new_selection — // Intentionally `id`, not `new_selection`: the fallback-to-last-tab path must not
// preserving that the fallback-to-last-tab path never triggers this render call // trigger this render even when the last selected tab was Prepare.
// even if the last selected tab happened to be Prepare. Do not "simplify" to
// new_selection == TAB_ID_PREPARE, that changes behavior.
if (id == TAB_ID_PREPARE && m_layout == ESettingsLayout::Old) if (id == TAB_ID_PREPARE && m_layout == ESettingsLayout::Old)
m_plater->canvas3D()->render(); m_plater->canvas3D()->render();
else if (was_hidden) { else if (was_hidden) {
+2
View File
@@ -362,6 +362,8 @@ public:
//SoftFever //SoftFever
void show_device(bool should_use_native); void show_device(bool should_use_native);
void fit_tab_labels(); // ORCA void fit_tab_labels(); // ORCA
// True while either of the two tabs backed by m_plater is selected.
bool is_prepare_or_preview_tab() const;
PluginPages& plugin_pages() { return m_plugin_pages; } PluginPages& plugin_pages() { return m_plugin_pages; }
PA_Calibration_Dlg* m_pa_calib_dlg{ nullptr }; PA_Calibration_Dlg* m_pa_calib_dlg{ nullptr };
+3 -21
View File
@@ -156,14 +156,13 @@ void ButtonsListCtrl::SetSelection(int sel)
Refresh(); Refresh();
} }
bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /* = false*/, const std::string &bmp_name /* = ""*/, int imageId /* = wxBookCtrlBase::NO_IMAGE */) bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /* = false*/, const std::string &bmp_name /* = ""*/, const wxBitmap &bmp /* = wxNullBitmap */)
{ {
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 && if (bmp_name.empty() && bmp.IsOk())
imageId < m_imageList->GetImageCount()) btn->SetIcon(bmp);
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
@@ -232,23 +231,6 @@ 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];
+51 -135
View File
@@ -3,11 +3,11 @@
//#ifdef _WIN32 //#ifdef _WIN32
#include <initializer_list>
#include <string> #include <string>
#include <vector> #include <vector>
#include <string>
#include <wx/bookctrl.h> #include <wx/bookctrl.h>
#include <wx/imaglist.h> #include <wx/bitmap.h>
#include <wx/sizer.h> #include <wx/sizer.h>
class ScalableButton; class ScalableButton;
@@ -27,11 +27,9 @@ 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 = "", int imageId = wxBookCtrlBase::NO_IMAGE); bool InsertPage(size_t n, const wxString &text, bool bSelect = false, const std::string &bmp_name = "", const wxBitmap &bmp = wxNullBitmap);
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;
@@ -49,23 +47,12 @@ 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};
wxWindow* m_overflow_button{nullptr}; // ORCA wxWindow* m_overflow_button{nullptr}; // ORCA
}; };
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,
@@ -156,83 +143,58 @@ public:
// Implement base class pure virtual methods. // Implement base class pure virtual methods.
// Page management. Every insertion funnels through the InsertPage() below; `id` is the
// stable page name FindPageByName() resolves. Built-in tabs name a resource bitmap,
// plugin pages hand over a ready wxBitmap; wx's own imageId overloads carry neither.
bool AddPage(const wxString& id,
wxWindow* page,
const wxString& text,
const std::string& bmp_name = "",
bool bSelect = false)
{
DoInvalidateBestSize();
return InsertPage(GetPageCount(), id, page, text, bmp_name, bSelect);
}
bool AddPage(wxWindow* page, const wxString& text, bool bSelect = false, int imageId = NO_IMAGE) override bool AddPage(wxWindow* page, const wxString& text, bool bSelect = false, int imageId = NO_IMAGE) override
{ {
DoInvalidateBestSize(); DoInvalidateBestSize();
return InsertPage(GetPageCount(), page, text, bSelect, imageId); return InsertPage(GetPageCount(), page, text, bSelect, imageId);
} }
// Page management
virtual bool InsertPage(size_t n,
wxWindow * page,
const wxString & text,
bool bSelect = false,
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))
return false;
m_pageNames.insert(m_pageNames.begin() + n, id);
m_pageImageIds.insert(m_pageImageIds.begin() + n, imageId);
GetBtnsListCtrl()->InsertPage(n, text, bSelect, "", imageId);
if (!DoSetSelectionAfterInsertion(n, bSelect))
page->Hide();
return true;
}
bool InsertPage(size_t n, bool InsertPage(size_t n,
const wxString& id, const wxString& id,
wxWindow * page, wxWindow * page,
const wxString & text, const wxString & text,
const std::string& bmp_name = "", const std::string& bmp_name = "",
bool bSelect = false) bool bSelect = false,
const wxBitmap& bmp = wxNullBitmap)
{ {
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);
m_pageImageIds.insert(m_pageImageIds.begin() + n, NO_IMAGE); GetBtnsListCtrl()->InsertPage(n, text, bSelect, bmp_name, bmp);
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
// new page to the current page's rect — it never touches visibility. A freshly // page to the current page's rect — it never touches visibility, and a freshly
// constructed page defaults to shown, so without this it renders on top of // constructed page defaults to shown. Without this it renders on top of whatever
// whatever page is currently selected until the next SetSelection() call hides // page is currently selected until the next SetSelection() call hides it.
// it. Mirrors the pure-virtual InsertPage() override above, which already does
// this correctly.
if (!DoSetSelectionAfterInsertion(n, bSelect)) if (!DoSetSelectionAfterInsertion(n, bSelect))
page->Hide(); page->Hide();
return true; return true;
} }
virtual bool InsertPage(size_t n,
wxWindow * page,
const wxString & text,
bool bSelect = false,
int WXUNUSED(imageId) = NO_IMAGE) override
{
return InsertPage(n, wxString(), page, text, "", bSelect);
}
virtual int SetSelection(size_t n) override virtual int SetSelection(size_t n) override
{ {
int ret = DoSetSelection(n, SetSelection_SendEvent); int ret = DoSetSelection(n, SetSelection_SendEvent);
@@ -262,7 +224,8 @@ public:
return DoSetSelection(n); return DoSetSelection(n);
} }
// Labels are stored by the custom button list; page images use the wx image-list IDs below. // Labels are stored by the custom button list; wx's image-list API is unused — tab icons
// are set directly on the buttons, either from a resource name or a ready wxBitmap.
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"));
@@ -278,27 +241,14 @@ public:
return GetBtnsListCtrl()->GetPageText(n); return GetBtnsListCtrl()->GetPageText(n);
} }
virtual bool SetPageImage(size_t n, int imageId) override virtual bool SetPageImage(size_t WXUNUSED(n), int WXUNUSED(imageId)) override
{ {
if (n >= m_pageImageIds.size()) return false;
return false;
if (!GetBtnsListCtrl()->SetPageImage(n, imageId))
return false;
m_pageImageIds[n] = imageId;
return true;
} }
virtual int GetPageImage(size_t n) const override virtual int GetPageImage(size_t WXUNUSED(n)) const override
{ {
return n < m_pageImageIds.size() ? m_pageImageIds[n] : NO_IMAGE; return 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)
@@ -314,22 +264,27 @@ public:
page->SetFocus(); page->SetFocus();
} }
// wxBookCtrlBase::DeleteAllPages() clears its page list directly rather than // The base clears its page list directly instead of calling DoRemovePage() per page,
// going through DoRemovePage() per page, so it would otherwise leave // which would leave m_pageNames behind. No caller today; kept in sync regardless.
// m_pageNames desynchronized (a mutation path outside the four this class
// already keeps in sync). Not currently called on a Notebook anywhere in
// this codebase, but kept correct for the same reason the rest of this
// bookkeeping exists.
virtual bool DeleteAllPages() override virtual bool DeleteAllPages() override
{ {
m_pageNames.clear(); m_pageNames.clear();
m_pageImageIds.clear();
return wxBookCtrlBase::DeleteAllPages(); return wxBookCtrlBase::DeleteAllPages();
} }
ButtonsListCtrl* GetBtnsListCtrl() const { return static_cast<ButtonsListCtrl*>(m_bookctrl); } ButtonsListCtrl* GetBtnsListCtrl() const { return static_cast<ButtonsListCtrl*>(m_bookctrl); }
void SetOverflowButton(wxWindow* button) { GetBtnsListCtrl()->SetOverflowButton(button); } void SetOverflowButton(wxWindow* button) { GetBtnsListCtrl()->SetOverflowButton(button); }
// Insertion index just past the first of `ids` that is present, or the end of the bar
// if none is — lets call sites state tab order as "after X" instead of re-deriving it.
size_t PositionAfter(std::initializer_list<const char*> ids) const
{
for (const char* id : ids)
if (const int idx = FindPageByName(id); idx != wxNOT_FOUND)
return static_cast<size_t>(idx) + 1;
return GetPageCount();
}
int FindPageByName(const wxString& id) const int FindPageByName(const wxString& id) const
{ {
if (id.empty()) if (id.empty())
@@ -485,7 +440,6 @@ 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);
} }
@@ -509,47 +463,9 @@ 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;
+5 -6
View File
@@ -11221,19 +11221,18 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
const int new_sel = e.GetSelection(); const int new_sel = e.GetSelection();
if (new_sel == wxNOT_FOUND) { if (new_sel == wxNOT_FOUND) {
// Guards against new_sel matching FindPageByName's own wxNOT_FOUND sentinel // GetPage(new_sel) below needs a valid index.
// below when a TAB_ID_* isn't currently present in the tabpanel.
e.Skip(); e.Skip();
return; return;
} }
sidebar_layout.show = new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_PREPARE) || const wxString new_name = main_frame->m_tabpanel->GetPageName(new_sel);
new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_PREVIEW); sidebar_layout.show = new_name == TAB_ID_PREPARE || new_name == TAB_ID_PREVIEW;
update_sidebar(); update_sidebar();
int old_sel = e.GetOldSelection(); int old_sel = e.GetOldSelection();
const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents"); const bool use_printer_agents = wxGetApp().app_config->get_bool("use_printer_agents");
const bool use_native_device_tab = wxGetApp().preset_bundle && const bool use_native_device_tab = wxGetApp().preset_bundle &&
(wxGetApp().preset_bundle->use_bbl_device_tab() || use_printer_agents); (wxGetApp().preset_bundle->use_bbl_device_tab() || use_printer_agents);
if (use_native_device_tab && new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_MONITOR)) { if (use_native_device_tab && new_name == TAB_ID_MONITOR) {
// BBL network module is only required for BBL-vendor printers. // BBL network module is only required for BBL-vendor printers.
// Non-BBL Python plugins (e.g. moonraker) drive the Device tab without it. // Non-BBL Python plugins (e.g. moonraker) drive the Device tab without it.
if (!use_printer_agents && wxGetApp().preset_bundle->is_bbl_vendor() && !Slic3r::NetworkAgent::is_network_module_loaded()) { if (!use_printer_agents && wxGetApp().preset_bundle->is_bbl_vendor() && !Slic3r::NetworkAgent::is_network_module_loaded()) {
@@ -11253,7 +11252,7 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
if (selecting_web_device_tab) { if (selecting_web_device_tab) {
// Use the selected discovered machine when the preset has no host. // Use the selected discovered machine when the preset has no host.
main_frame->load_printer_url(); main_frame->load_printer_url();
} else if (new_sel == main_frame->m_tabpanel->FindPageByName(TAB_ID_MONITOR) && wxGetApp().preset_bundle != nullptr) { } else if (new_name == TAB_ID_MONITOR && wxGetApp().preset_bundle != nullptr) {
auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config; auto cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
wxString url = from_u8(PrintHost::get_print_host_webui(&cfg)); wxString url = from_u8(PrintHost::get_print_host_webui(&cfg));
if (main_frame->m_printer_view && url.empty()) { if (main_frame->m_printer_view && url.empty()) {
+1 -1
View File
@@ -288,7 +288,7 @@ void Button::render(wxDC& dc)
wxSize szIcon; wxSize szIcon;
wxSize textSize = this->textSize.GetSize(); wxSize textSize = this->textSize.GetSize();
ScalableBitmap icon = active_icon; const ScalableBitmap& icon = active_icon;
wxSize padding = this->paddingSize; wxSize padding = this->paddingSize;
int spacing = 5; int spacing = 5;
// Wrap text // Wrap text
+49 -58
View File
@@ -171,9 +171,11 @@ void PluginPage::on_script_message(wxWebViewEvent& event)
root.value("kind", std::string()) != "message") root.value("kind", std::string()) != "message")
return; return;
const nlohmann::json data = root.contains("data") ? root["data"] : nlohmann::json(); const auto data = root.find("data");
try { try {
m_cap->on_message(data.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)); m_cap->on_message(data == root.end()
? "null"
: data->dump(-1, ' ', false, nlohmann::json::error_handler_t::replace));
} catch (const std::exception& error) { } catch (const std::exception& error) {
BOOST_LOG_TRIVIAL(error) << "Plugin page message handler failed for '" << m_cap->name() << "': " << error.what(); BOOST_LOG_TRIVIAL(error) << "Plugin page message handler failed for '" << m_cap->name() << "': " << error.what();
} catch (...) { } catch (...) {
@@ -186,28 +188,23 @@ void PluginPage::push_message(const std::string& message)
if (m_browser == nullptr) if (m_browser == nullptr)
return; return;
nlohmann::json data = nlohmann::json::parse(message, nullptr, false); // PagesPluginCapability::post_message() already dumps JSON, so accept it as-is; only a
if (data.is_discarded()) // non-JSON payload needs wrapping as a string literal.
data = message; const std::string payload = nlohmann::json::accept(message)
? message
: nlohmann::json(message).dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
const wxString script = wxString("(function dispatch(payload, attempts) {\n") + WebView::RunScript(m_browser, wxString::Format(
wxString(" if (typeof window.__orcaDispatch === 'function') { window.__orcaDispatch(payload); return; }\n") + "(function dispatch(payload, attempts) {\n"
wxString(" if (attempts < 100) window.setTimeout(function() { dispatch(payload, attempts + 1); }, 25);\n") + " if (typeof window.__orcaDispatch === 'function') { window.__orcaDispatch(payload); return; }\n"
wxString("})({data: ") + " if (attempts < 100) window.setTimeout(function() { dispatch(payload, attempts + 1); }, 25);\n"
wxString::FromUTF8(data.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)) + "})({data: %s}, 0);",
wxString("}, 0);"); wxString::FromUTF8(payload)));
WebView::RunScript(m_browser, script);
} }
PluginPages::~PluginPages() PluginPages::~PluginPages()
{ {
shutdown(); shutdown();
// try {
// } catch (const std::exception& error) {
// BOOST_LOG_TRIVIAL(error) << "PluginPages::~PluginPages: shutdown() threw: " << error.what();
// } catch (...) {
// BOOST_LOG_TRIVIAL(error) << "PluginPages::~PluginPages: shutdown() threw a non-standard exception";
// }
} }
void PluginPages::initialize(Notebook* parent) void PluginPages::initialize(Notebook* parent)
@@ -219,9 +216,6 @@ void PluginPages::initialize(Notebook* parent)
m_visible_page_count = GUI::wxGetApp().app_config->get_plugin_pages_visible_count(); m_visible_page_count = GUI::wxGetApp().app_config->get_plugin_pages_visible_count();
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)
create_page(capability->identity()); create_page(capability->identity());
@@ -233,15 +227,12 @@ 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;
} }
void PluginPages::set_visible_page_count(int count) void PluginPages::set_visible_page_count(int count)
{ {
const int clamped = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(count, PLUGIN_PAGES_VISIBLE_COUNT_MAX)); const int clamped = std::clamp(count, PLUGIN_PAGES_VISIBLE_COUNT_MIN, PLUGIN_PAGES_VISIBLE_COUNT_MAX);
if (clamped == m_visible_page_count) if (clamped == m_visible_page_count)
return; return;
@@ -282,17 +273,14 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
return false; return false;
} }
int image_id = wxBookCtrlBase::NO_IMAGE; if (!icon.empty()) {
if (!icon.empty() && m_image_list) {
try { try {
boost::filesystem::path icon_path(icon); boost::filesystem::path icon_path(icon);
const std::string extension = icon_path.extension().string(); const std::string extension = icon_path.extension().string();
if (extension == ".svg" || extension == ".png") if (extension == ".svg" || extension == ".png")
icon_path.replace_extension(); icon_path.replace_extension();
const wxBitmap bitmap = create_scaled_bitmap(icon_path.string(), m_parent, 20); page->set_icon(create_scaled_bitmap(icon_path.string(), m_parent, 20));
if (bitmap.IsOk())
image_id = m_image_list->Add(bitmap);
} catch (const std::exception& error) { } catch (const std::exception& error) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what(); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what();
} catch (...) { } catch (...) {
@@ -300,7 +288,6 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
} }
} }
page->set_icon_image_id(image_id);
m_pages.emplace(id, page); m_pages.emplace(id, page);
m_order.push_back(id); m_order.push_back(id);
return true; return true;
@@ -349,24 +336,11 @@ 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();
m_pages.erase(it); m_pages.erase(it);
m_order.erase(std::remove(m_order.begin(), m_order.end(), id), m_order.end()); m_order.erase(std::remove(m_order.begin(), m_order.end(), id), m_order.end());
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.
for (auto& [other_id, other_page] : m_pages) {
const int other_image_id = other_page->get_icon_image_id();
if (other_image_id > removed_image_id)
other_page->set_icon_image_id(other_image_id - 1);
}
}
const int idx = m_parent != nullptr ? m_parent->FindPage(page) : wxNOT_FOUND; const int idx = m_parent != nullptr ? m_parent->FindPage(page) : wxNOT_FOUND;
if (idx != wxNOT_FOUND) if (idx != wxNOT_FOUND)
m_parent->RemovePage(idx); m_parent->RemovePage(idx);
@@ -394,14 +368,6 @@ void PluginPages::relayout()
}), }),
m_order.end()); m_order.end());
wxString id_to_reselect = m_parent->GetSelectedPageName();
for (const auto& [id, page] : m_pages) {
const int idx = m_parent->FindPage(page);
if (idx != wxNOT_FOUND)
m_parent->RemovePage(idx);
}
const int visible_slots = std::max(1, m_visible_page_count); const int visible_slots = std::max(1, m_visible_page_count);
const bool need_overflow = static_cast<int>(m_order.size()) > visible_slots; const bool need_overflow = static_cast<int>(m_order.size()) > visible_slots;
@@ -421,9 +387,37 @@ void PluginPages::relayout()
tab_ids.push_back(*m_swapped_in_id); tab_ids.push_back(*m_swapped_in_id);
} }
for (const auto& id : tab_ids) { // MainFrame::show_device() relayouts on every printer change and most of those change
PluginPage* page = m_pages.at(id); // nothing, so only touch the notebook when the trailing slots don't already spell out
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), page, wxString::FromUTF8(id.name), page->get_icon_image_id()); // tab_ids — a rebuild destroys and recreates every tab button and rasterizes every icon.
const size_t page_count = m_parent->GetPageCount();
bool up_to_date = page_count >= tab_ids.size();
for (size_t i = 0; up_to_date && i < tab_ids.size(); ++i)
up_to_date = m_parent->GetPageName(page_count - tab_ids.size() + i) == page_tab_id(tab_ids[i]);
for (const auto& [id, page] : m_pages) {
if (!up_to_date)
break;
const bool wanted = std::find(tab_ids.begin(), tab_ids.end(), id) != tab_ids.end();
up_to_date = (m_parent->FindPage(page) != wxNOT_FOUND) == wanted;
}
if (!up_to_date) {
const wxString id_to_reselect = m_parent->GetSelectedPageName();
for (const auto& [id, page] : m_pages) {
const int idx = m_parent->FindPage(page);
if (idx != wxNOT_FOUND)
m_parent->RemovePage(idx);
}
for (const auto& id : tab_ids) {
PluginPage* page = m_pages.at(id);
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), page, wxString::FromUTF8(id.name), "",
false, page->icon());
}
if (!id_to_reselect.empty())
m_parent->SelectPageByName(id_to_reselect);
} }
if (need_overflow) { if (need_overflow) {
@@ -442,9 +436,6 @@ void PluginPages::relayout()
m_overflow_button->Destroy(); m_overflow_button->Destroy();
m_overflow_button = nullptr; m_overflow_button = nullptr;
} }
if (!id_to_reselect.empty())
m_parent->SelectPageByName(id_to_reselect);
} }
void PluginPages::show_overflow_menu() void PluginPages::show_overflow_menu()
+4 -8
View File
@@ -10,8 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <wx/bookctrl.h> #include <wx/bitmap.h>
#include <wx/imaglist.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/webview.h> #include <wx/webview.h>
@@ -33,8 +32,8 @@ 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; } void set_icon(const wxBitmap& icon) { m_icon = icon; }
int get_icon_image_id() const { return m_icon_image_id; } const wxBitmap& icon() const { return m_icon; }
private: private:
void load_plugin_content(); void load_plugin_content();
@@ -45,8 +44,7 @@ private:
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};
wxBitmap m_icon;
int m_icon_image_id = wxBookCtrlBase::NO_IMAGE;
}; };
class PluginPages class PluginPages
@@ -66,7 +64,6 @@ public:
void on_plugin_register(const std::string& plugin_key); void on_plugin_register(const std::string& plugin_key);
void on_plugin_deregister(const std::string& plugin_key); void on_plugin_deregister(const std::string& plugin_key);
int get_visible_page_count() const { return m_visible_page_count; }
void set_visible_page_count(int count); void set_visible_page_count(int count);
void relayout(); void relayout();
@@ -83,7 +80,6 @@ private:
std::vector<PluginCapabilityId> m_order; std::vector<PluginCapabilityId> m_order;
Notebook* m_parent{nullptr}; Notebook* m_parent{nullptr};
std::unique_ptr<wxImageList> m_image_list;
int m_visible_page_count{0}; int m_visible_page_count{0};
std::optional<PluginCapabilityId> m_swapped_in_id; std::optional<PluginCapabilityId> m_swapped_in_id;