feat: ams dropdown in sidebar

This commit is contained in:
Ian Chua committed 2026-10-19 18:32:18 +08:00
1 parent f38f2bddec
commit 53d7b87db5
1 file changed
+344 -81
+344 -81
View File
@@ -773,11 +773,13 @@ struct ExtruderGroup : StaticBox
Label * prefix = nullptr;
Label * diameter_label = nullptr; // read-only nozzle diameter of this extruder
ComboBox * flow = nullptr;
wxBoxSizer *row_sizer = nullptr; // the row's line, moved between the two columns as needed
};
ExtruderGroup(wxWindow * parent, int index, wxString const &title);
wxBoxSizer * sizer = nullptr;
HoverLabel * hover_label = nullptr;
wxPanel * ams_panel = nullptr;
wxStaticText* ams_label{nullptr};
ScalableButton * btn_edit = nullptr;
ComboBox * combo_diameter = nullptr;
@@ -786,6 +788,8 @@ struct ExtruderGroup : StaticBox
ScalableButton * btn_up{nullptr};
ScalableButton * btn_down{nullptr};
wxBoxSizer *hsizer_ams { nullptr };
wxBoxSizer *ams_section { nullptr }; // AMS panel + its spacing, shown independently of the nozzle rows
bool ams_visible{false};
size_t page_cur{0};
size_t page_num{3};
size_t ams_n4 = 0;
@@ -795,13 +799,30 @@ struct ExtruderGroup : StaticBox
wxString diameter;
std::vector<NozzleRow> rows; // rows[0] always exists, one row per extruder when multi
size_t nozzle_row_count = 0; // rows currently shown
size_t nozzle_row_count = 0; // rows currently configured
int visible_row = -1; // -1 shows every configured row, otherwise the only row shown
wxBoxSizer * rows_sizer = nullptr;
wxBoxSizer * row_columns[2] = {}; // row i is dealt into column i % 2
bool rows_single_column = false;
int group_index = -1; // ctor index, the extruder a single-row group edits
// Show `count` nozzle rows, one per extruder, each prefixed with T<index> when count > 1.
void SetNozzleRowCount(size_t count);
size_t NozzleRowCount() const { return nozzle_row_count; }
// Generic multi-tool cards list one tool at a time while keeping every configured row alive.
bool IsNozzleRowVisible(size_t row) const
{
return row < nozzle_row_count && (visible_row < 0 || row == size_t(visible_row));
}
void SetVisibleNozzleRow(int index);
// Re-apply row visibility, re-measure and lay the card out after the row selection changed.
void apply_nozzle_row_visibility();
// A single-tool card shows its one row across the whole width; a listing deals rows into columns.
void restack_nozzle_rows();
// The AMS section is hidden on the listing card used for a single nozzle or a plain multi-row list.
void SetAmsVisible(bool show);
// Restart paging when the card starts showing another tool.
void ResetAmsPage();
// Nozzle diameter readout of the shown rows, one value per extruder. A listing card cannot edit
// it: the diameter belongs to the machine variant the printer preset selects, not to the extruder.
void SetRowDiameters(const std::vector<double> &diameters);
@@ -817,7 +838,7 @@ struct ExtruderGroup : StaticBox
return;
ams_n4 = n4;
ams_n1 = n1;
if (btn_edit) {
if (btn_edit || ams_visible) {
update_ams();
}
}
@@ -828,7 +849,7 @@ struct ExtruderGroup : StaticBox
void SetEditEnabled(bool enable) { if (hover_label) hover_label->EnableEdit(enable); }
void SetOnHoverClick(std::function<void()> on_click) { if (hover_label) hover_label->SetOnHoverClick(std::move(on_click)); }
void sync_ams(MachineObject const *obj, std::vector<DevAms *> const &ams4, std::vector<DevAms *> const &ams1);
void sync_ams(MachineObject const *obj, std::vector<DevAms *> const &ams4, std::vector<DevAms *> const &ams1, bool live_count = false);
void Rescale()
{
@@ -906,8 +927,17 @@ struct Sidebar::priv
ExtruderGroup *right_extruder = nullptr;
ExtruderGroup *single_extruder = nullptr;
// Generic printers with more than two extruders show one tool at a time: a read-only selector
// above the card and the selected tool's nozzle row and AMS section inside it.
wxPanel * panel_extruder_tool = nullptr;
ComboBox * combo_extruder_tool = nullptr;
int selected_tool = 0;
bool generic_multi_tool_layout = false;
int FromDIP(int n) { return plater->FromDIP(n); }
void layout_printer(bool isBBL, bool isDual);
// Refresh the tool choices and keep `selected_tool` valid when the extruder count changes.
void update_tool_selector(int extruder_count, bool generic_multi);
void flush_printer_sync(bool restart = false);
@@ -1081,11 +1111,42 @@ void Sidebar::priv::layout_printer(bool isBBL, bool isDual)
});
}
// Tool selector for a generic printer with more than two extruders. The card below shows only
// the selected tool; every other layout keeps the selector hidden.
panel_extruder_tool = new wxPanel(m_panel_printer_content, wxID_ANY);
panel_extruder_tool->SetBackgroundColour(*wxWHITE);
combo_extruder_tool = new ComboBox(panel_extruder_tool, wxID_ANY, wxString(""), wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
combo_extruder_tool->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
const int selection = combo_extruder_tool->GetSelection();
if (selection < 0 || !generic_multi_tool_layout)
return;
selected_tool = selection;
single_extruder->SetVisibleNozzleRow(selection);
single_extruder->SetTitle(wxString::Format(_L("Tool %d"), selected_tool + 1));
if (auto *plater = GUI::wxGetApp().plater())
plater->update_machine_sync_status();
single_extruder->ResetAmsPage();
if (m_panel_printer_content)
m_panel_printer_content->Layout();
});
use_nozzle_row_font(combo_extruder_tool);
auto *tool_label = new Label(panel_extruder_tool, _L("Tool"));
tool_label->SetFont(Label::Body_13);
auto *tool_row = new wxBoxSizer(wxHORIZONTAL);
tool_row->Add(tool_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(5));
tool_row->Add(combo_extruder_tool, 1, wxALIGN_CENTER_VERTICAL);
auto *tool_panel_sizer = new wxBoxSizer(wxVERTICAL);
tool_panel_sizer->Add(tool_row, 0, wxEXPAND);
tool_panel_sizer->AddSpacer(FromDIP(4)); // gap above the card, only present while the selector is shown
panel_extruder_tool->SetSizer(tool_panel_sizer);
panel_extruder_tool->Hide();
// single
extruder_single_sizer = new wxBoxSizer(wxHORIZONTAL);
extruder_single_sizer->Add(single_extruder, 1, wxEXPAND, 0);
wxBoxSizer * extruder_sizer = new wxBoxSizer(wxVERTICAL);
extruder_sizer->Add(panel_extruder_tool, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(SidebarProps::ContentMargin()));
extruder_sizer->Add(extruder_dual_sizer , 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(SidebarProps::ContentMargin()));
extruder_sizer->Add(extruder_single_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(SidebarProps::ContentMargin()));
@@ -1115,19 +1176,37 @@ void Sidebar::priv::layout_printer(bool isBBL, bool isDual)
int extruder_count = 0;
const bool has_flow_variant = cfg.support_different_extruders(extruder_count);
// ORCA: a non-Bambu printer with several extruders lists one nozzle row per tool; Bambu's own
// multi-extruder printers keep the left/right pair above.
// ORCA: a non-Bambu printer with several extruders lists nozzle rows per tool; Bambu's own
// multi-extruder printers keep the left/right pair above. Beyond two tools the card shows one
// tool at a time, chosen with the selector, so the AMS section has room to stay visible.
const bool multi_extruder_rows = !preset_bundle.is_bbl_vendor() && extruder_count > 1;
const bool generic_multi_tool = multi_extruder_rows && extruder_count > 2;
update_tool_selector(extruder_count, generic_multi_tool);
// The variant box switches the machine variant. The card below lists the nozzles when there
// are flow variants to choose or several extruders to show; otherwise the box says it all.
panel_nozzle_dia->Show(true);
extruder_single_sizer->Show(has_flow_variant || multi_extruder_rows);
single_extruder->SetNozzleRowCount(multi_extruder_rows ? size_t(extruder_count) : 1);
if (generic_multi_tool) {
// Keep every tool row configured (see update_all_presets) but display only the selected
// one, together with that tool's AMS previews.
single_extruder->SetNozzleRowCount(size_t(extruder_count));
single_extruder->SetVisibleNozzleRow(selected_tool);
// The card may be carrying another printer's AMS until the device sync refills it.
single_extruder->sync_ams(nullptr, {}, {}, true);
single_extruder->SetAmsVisible(true);
} else {
single_extruder->SetNozzleRowCount(multi_extruder_rows ? size_t(extruder_count) : 1);
single_extruder->SetVisibleNozzleRow(-1);
single_extruder->SetAmsVisible(false);
}
} else {
update_tool_selector(0, false);
panel_nozzle_dia->Show(false);
extruder_single_sizer->Show(false);
single_extruder->SetNozzleRowCount(1);
single_extruder->SetVisibleNozzleRow(-1);
single_extruder->SetAmsVisible(false);
}
// ORCA ensure printer section is visible after changing printer from printer selection dialog
@@ -1141,6 +1220,44 @@ void Sidebar::priv::layout_printer(bool isBBL, bool isDual)
}
}
void Sidebar::priv::update_tool_selector(int extruder_count, bool generic_multi)
{
generic_multi_tool_layout = generic_multi;
if (!combo_extruder_tool)
return;
if (!generic_multi) {
selected_tool = 0;
single_extruder->SetTitle(_L("Nozzle"));
combo_extruder_tool->Hide();
if (panel_extruder_tool)
panel_extruder_tool->Hide();
if (m_panel_printer_content)
m_panel_printer_content->Layout();
return;
}
const int count = std::min(std::max(extruder_count, 1), int(MAXIMUM_EXTRUDER_NUMBER));
if (combo_extruder_tool->GetCount() != count) {
combo_extruder_tool->Clear();
for (int i = 0; i < count; ++i)
combo_extruder_tool->Append(wxString::Format("T%d", i + 1));
}
// A device refresh keeps the chosen tool; a preset change that removes it falls back to the last
// remaining tool rather than leaving a stale selection.
const int previous_tool = selected_tool;
selected_tool = std::clamp(selected_tool, 0, count - 1);
combo_extruder_tool->SetSelection(selected_tool);
single_extruder->SetTitle(wxString::Format(_L("Tool %d"), selected_tool + 1));
if (selected_tool != previous_tool)
single_extruder->ResetAmsPage();
if (panel_extruder_tool)
panel_extruder_tool->Show();
combo_extruder_tool->Show();
if (m_panel_printer_content)
m_panel_printer_content->Layout();
}
void Sidebar::priv::flush_printer_sync(bool restart)
{
if (restart) {
@@ -1546,7 +1663,7 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
use_nozzle_row_font(combo_diameter);
// AMS
auto ams_panel = new wxPanel(this, wxID_ANY);
ams_panel = new wxPanel(this, wxID_ANY);
ams_panel->SetBackgroundColour(*wxWHITE);
ams_label = new wxStaticText(ams_panel, wxID_ANY, _L("AMS"));
@@ -1631,18 +1748,19 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
vsizer->AddSpacer(FromDIP(16)); // spacing for title and control
if (index < 0) {
ams_panel->Hide();
} else {
vsizer->Add(ams_panel, 0, wxEXPAND | wxLEFT | wxRIGHT , FromDIP(5));
vsizer->AddSpacer(FromDIP(2));
}
ams_section = new wxBoxSizer(wxVERTICAL);
ams_section->Add(ams_panel, 0, wxEXPAND | wxLEFT | wxRIGHT , FromDIP(5));
ams_section->AddSpacer(FromDIP(2));
vsizer->Add(ams_section, 0, wxEXPAND);
ams_visible = index >= 0;
ams_panel->Show(ams_visible);
vsizer->Show(ams_section, ams_visible);
// Nozzle rows. The first one is the only one for a printer whose nozzles are not listed
// individually; a multi-extruder printer grows one row per tool (see SetNozzleRowCount).
// The rows are dealt into two columns, which halves the height of a long tool list; the second
// column empties itself when there is a single row, so a one-row card still spans the width.
auto rows_sizer = new wxBoxSizer(wxHORIZONTAL);
rows_sizer = new wxBoxSizer(wxHORIZONTAL);
for (wxBoxSizer *&column : row_columns) {
column = new wxBoxSizer(wxVERTICAL);
rows_sizer->Add(column, 1, wxEXPAND | wxLEFT , FromDIP(5));
@@ -1654,7 +1772,11 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
Layout();
AMSCountPopupWindow::UpdateAMSCount(index < 0 ? 0 : index, this);
// The indexed cards also maintain the two-entry AMS-count model used by the edit popup. The
// single/nozzle card is a listing card: its AMS previews come from the live unit list bound to
// the chosen tool instead, so it must not seed that model.
if (index >= 0)
AMSCountPopupWindow::UpdateAMSCount(index, this);
}
ExtruderGroup::NozzleRow ExtruderGroup::create_nozzle_row()
@@ -1698,8 +1820,11 @@ ExtruderGroup::NozzleRow ExtruderGroup::create_nozzle_row()
row_sizer->Add(combo_diameter, 1, wxRIGHT, FromDIP(5));
row_sizer->Add(row.diameter_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(5));
row_sizer->Add(row.flow, 1);
// Row-major: T1 T2 on the first line, T3 T4 on the second, and so on.
row_columns[row_index % 2]->Add(row_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(5));
row.row_sizer = row_sizer;
// Row-major: T1 T2 on the first line, T3 T4 on the second, and so on. A single-tool card keeps
// its one row on the first column so it spans the card width.
wxBoxSizer *column = rows_single_column ? row_columns[0] : row_columns[row_index % 2];
column->Add(row_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(5));
return row;
}
@@ -1740,22 +1865,56 @@ void ExtruderGroup::SetNozzleRowCount(size_t count)
if (row_count == nozzle_row_count)
return;
nozzle_row_count = row_count;
if (visible_row >= int(row_count))
visible_row = int(row_count) - 1;
for (size_t i = 0; i < row_count; ++i) {
if (i >= rows.size())
rows.push_back(create_nozzle_row());
}
apply_nozzle_row_visibility();
}
void ExtruderGroup::SetVisibleNozzleRow(int index)
{
const size_t shown_rows = std::min(rows.size(), nozzle_row_count);
visible_row = shown_rows == 0 || index < 0 ? -1 : std::min<int>(index, int(shown_rows) - 1);
apply_nozzle_row_visibility();
}
void ExtruderGroup::restack_nozzle_rows()
{
const bool single = visible_row >= 0;
if (rows_single_column == single || !rows_sizer || !row_columns[0] || !row_columns[1])
return;
for (const NozzleRow &row : rows) {
row_columns[0]->Detach(row.row_sizer);
row_columns[1]->Detach(row.row_sizer);
}
for (size_t i = 0; i < rows.size(); ++i) {
wxBoxSizer *column = single ? row_columns[0] : row_columns[i % 2];
column->Add(rows[i].row_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(5));
}
rows_sizer->Show(row_columns[1], !single);
rows_single_column = single;
}
void ExtruderGroup::apply_nozzle_row_visibility()
{
restack_nozzle_rows();
// Every row names its tool as soon as there is more than one, so a single-nozzle card keeps the
// plain "Nozzle" look. A row is shown by showing its widgets: a sizer item holding a sub-sizer
// reports itself as shown while any of its windows is, so hiding the sub-sizer alone would
// leave the row in the layout.
for (size_t i = 0; i < row_count; ++i) {
if (i >= rows.size())
rows.push_back(create_nozzle_row());
rows[i].prefix->Show(row_count > 1);
rows[i].diameter_label->Show(group_index < 0); // the dual left/right cards show combo_diameter
// The flow combo's own visibility is decided with its choices, see update_extruder_variant.
}
for (size_t i = row_count; i < rows.size(); ++i) {
rows[i].prefix->Hide();
rows[i].diameter_label->Hide();
rows[i].flow->Hide();
for (size_t i = 0; i < rows.size(); ++i) {
const bool show = IsNozzleRowVisible(i);
rows[i].prefix->Show(show && nozzle_row_count > 1);
rows[i].diameter_label->Show(show && group_index < 0); // the dual left/right cards show combo_diameter
// The flow combo's own visibility follows its choices and the selected row, see update_extruder_variant.
rows[i].flow->Show(show && rows[i].flow->GetCount() > 0);
}
// The rows just changed: re-measure them, then let the card and its containers give it the new room.
@@ -1768,6 +1927,33 @@ void ExtruderGroup::SetNozzleRowCount(size_t count)
}
}
void ExtruderGroup::SetAmsVisible(bool show)
{
if (ams_visible == show)
return;
ams_visible = show;
if (ams_panel)
ams_panel->Show(show);
if (wxSizer *vsizer = GetSizer(); vsizer && ams_section)
vsizer->Show(ams_section, show);
if (show)
update_ams();
Layout();
if (wxWindow *parent = GetParent()) {
parent->Layout();
if (wxWindow *grand_parent = parent->GetParent())
grand_parent->Layout();
}
}
void ExtruderGroup::ResetAmsPage()
{
page_cur = 0;
if (ams_visible)
update_ams();
}
void ExtruderGroup::update_ams()
{
static AMSinfo info4;
@@ -1778,10 +1964,16 @@ void ExtruderGroup::update_ams()
info1.cans.push_back({});
}
if (btn_edit == nullptr)
if (!ams_visible)
return;
page_num = (ams_n4 * 2 + ams_n1 + 3) / 4;
// The AMS set can shrink when another tool is selected or the device changes; never leave the
// page pointer behind the last page.
if (page_num == 0)
page_cur = 0;
else if (page_cur >= page_num)
page_cur = page_num - 1;
size_t i4 = page_cur * 2;
size_t i1 = 0;
if (i4 > ams_n4) {
@@ -1808,8 +2000,12 @@ void ExtruderGroup::update_ams()
btn_up->Show(page_cur > 0);
btn_down->Show(page_cur + 1 < page_num);
while (hsizer_ams->GetItemCount() > 2)
hsizer_ams->Remove(2);
// Rebuild the row instead of trimming a fixed prefix: the listing card has no edit button, and
// the installed/not-installed message is reused across updates.
hsizer_ams->Clear(false);
hsizer_ams->Add(ams_label, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(5));
if (btn_edit)
hsizer_ams->Add(btn_edit, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(5));
if (ams_not_installed_msg->IsShown()) {
hsizer_ams->AddStretchSpacer(1);
hsizer_ams->Add(ams_not_installed_msg, 0, wxALIGN_CENTER);
@@ -1835,27 +2031,41 @@ void ExtruderGroup::update_ams()
}
}
// The row was rebuilt: lay its contents out before the card sizes itself around them.
ams_panel->Layout();
Layout();
}
void ExtruderGroup::sync_ams(MachineObject const *obj, std::vector<DevAms *> const &ams4, std::vector<DevAms *> const &ams1)
void ExtruderGroup::sync_ams(MachineObject const *obj, std::vector<DevAms *> const &ams4, std::vector<DevAms *> const &ams1, bool live_count)
{
if (ams_4.empty() && ams4.empty()
&& ams_1.empty() && ams1.empty())
return;
auto sync = [obj](std::vector<AMSinfo> &infos, std::vector<DevAms *> const &ams) -> bool {
std::vector<AMSinfo> infos2;
for (auto a : ams) {
AMSinfo ams_info;
ams_info.parse_ams_info(const_cast<MachineObject*>(obj), a, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity);
infos2.push_back(ams_info);
}
if (infos == infos2)
return false;
infos.swap(infos2);
return true;
};
if (sync(ams_4, ams4) || sync(ams_1, ams1))
bool changed = false;
if (!ams_4.empty() || !ams4.empty()
|| !ams_1.empty() || !ams1.empty()) {
auto sync = [obj](std::vector<AMSinfo> &infos, std::vector<DevAms *> const &ams) -> bool {
std::vector<AMSinfo> infos2;
for (auto a : ams) {
AMSinfo ams_info;
ams_info.parse_ams_info(const_cast<MachineObject*>(obj), a, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity);
infos2.push_back(ams_info);
}
if (infos == infos2)
return false;
infos.swap(infos2);
return true;
};
const bool ams4_changed = sync(ams_4, ams4);
const bool ams1_changed = sync(ams_1, ams1);
changed = ams4_changed || ams1_changed;
}
// A card fed from its own extruder binding derives its preview count from that list instead of
// the preset's AMS count.
if (live_count && (ams_n4 != ams4.size() || ams_n1 != ams1.size())) {
ams_n4 = ams4.size();
ams_n1 = ams1.size();
changed = true;
}
if (changed)
update_ams();
}
@@ -2378,6 +2588,8 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " go on sync_extruder_list";
const Preset &cur_preset = preset_bundle->printers.get_selected_preset();
int extruder_nums = preset_bundle->get_printer_extruder_count();
// The left/right cards and their two-entry AMS-count model do not describe an N-tool printer.
const bool generic_multi_tool = !preset_bundle->is_bbl_vendor() && extruder_nums > 2;
std::vector<int> extruder_map(extruder_nums);
std::iota(extruder_map.begin(), extruder_map.end(), 0);
const ConfigOptionInts *physical_extruder_map = cur_preset.config.option<ConfigOptionInts>("physical_extruder_map");
@@ -2512,23 +2724,25 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
}
}
if (extruder_nums > 1) {
if (nozzle_diameters_match) {
int left_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
int right_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[1]));
assert(left_index != -1 && right_index != -1);
left_extruder->combo_diameter->SetSelection(left_index);
right_extruder->combo_diameter->SetSelection(right_index);
}
AMSCountPopupWindow::SetAMSCount(deputy_index, deputy_4, deputy_1);
AMSCountPopupWindow::SetAMSCount(main_index, main_4, main_1);
AMSCountPopupWindow::UpdateAMSCount(0, left_extruder);
AMSCountPopupWindow::UpdateAMSCount(1, right_extruder);
} else {
if (nozzle_diameters_match) {
int index = single_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
assert(index != -1);
single_extruder->combo_diameter->SetSelection(index);
if (!generic_multi_tool) {
if (extruder_nums > 1) {
if (nozzle_diameters_match) {
int left_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
int right_index = left_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[1]));
assert(left_index != -1 && right_index != -1);
left_extruder->combo_diameter->SetSelection(left_index);
right_extruder->combo_diameter->SetSelection(right_index);
}
AMSCountPopupWindow::SetAMSCount(deputy_index, deputy_4, deputy_1);
AMSCountPopupWindow::SetAMSCount(main_index, main_4, main_1);
AMSCountPopupWindow::UpdateAMSCount(0, left_extruder);
AMSCountPopupWindow::UpdateAMSCount(1, right_extruder);
} else {
if (nozzle_diameters_match) {
int index = single_extruder->combo_diameter->FindString(get_diameter_string(nozzle_diameters[0]));
assert(index != -1);
single_extruder->combo_diameter->SetSelection(index);
}
}
}
@@ -2570,7 +2784,7 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
right_extruder->ShowBadge(false);
right_extruder->sync_ams(nullptr, {}, {});
single_extruder->ShowBadge(false);
single_extruder->sync_ams(nullptr, {}, {});
single_extruder->sync_ams(nullptr, {}, {}, true);
update_extruder_separator_icon(false, false);
//btn_sync_printer->SetBorderColor(not_synced_colour);
//btn_sync_printer->SetIcon("printer_sync");
@@ -2645,8 +2859,19 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
// 2. update extruder status
int extruder_nums = preset_bundle->get_printer_extruder_count();
if (extruder_nums != obj->GetExtderSystem()->GetTotalExtderCount())
// A generic printer with more than two tools shows one tool at a time. Its AMS units are
// attributed by their own extruder bindings; the Bambu left/right conventions do not apply.
const bool generic_multi_tool = !preset_bundle->is_bbl_vendor() && extruder_nums > 2;
if (extruder_nums != obj->GetExtderSystem()->GetTotalExtderCount()) {
// A count mismatch means the card cannot be trusted. Clear the previous device's previews
// and make sure the selected tool still exists instead of keeping a stale card.
clear_all_sync_status();
if (generic_multi_tool) {
update_tool_selector(extruder_nums, true);
single_extruder->SetVisibleNozzleRow(selected_tool);
}
return;
}
// Filament Track Switch: only ever surfaced on multi-extruder machines. When installed, tip/warn
// and show the status icon (green ready / red not-calibrated); when absent the icon stays hidden.
@@ -2695,6 +2920,13 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
right_extruder->diameter.ToDouble(&value);
extruder_infos[1].diameter = float(value);
}
else if (generic_multi_tool) {
// The selected card reads the nozzle diameter from the preset, one value per tool.
if (const auto *nozzle_diameter = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")) {
for (size_t i = 0; i < extruder_infos.size() && i < nozzle_diameter->values.size(); ++i)
extruder_infos[i].diameter = float(nozzle_diameter->values[i]);
}
}
std::vector<ExtruderInfo> machine_extruder_infos(obj->GetExtderSystem()->GetTotalExtderCount());
@@ -2704,6 +2936,31 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
machine_extruder_infos[extruder.GetExtId()].diameter = extruder.GetNozzleDiameter();
}
for (auto &item : obj->GetFilaSystem()->GetAmsList()) {
auto add_to_extruder = [&machine_extruder_infos](DevAms *ams, int extruder_id) {
if (extruder_id < 0 || extruder_id >= int(machine_extruder_infos.size()))
return;
if (ams->GetAmsType() == DevAms::N3S) { // N3S
machine_extruder_infos[extruder_id].ams_1++;
machine_extruder_infos[extruder_id].ams_v1.push_back(ams);
} else {
machine_extruder_infos[extruder_id].ams_4++;
machine_extruder_infos[extruder_id].ams_v4.push_back(ams);
}
};
if (generic_multi_tool) {
// Generic agents report the bound extruder on every AMS unit. Honour the whole binding
// set so a unit the device binds to several tools appears on each of them. Bindings are
// 0-based tool indices, matching selected_tool (T1 == 0).
const std::set<int> &bindings = item.second->GetBindedExtruderSet();
if (bindings.empty())
add_to_extruder(item.second, item.second->GetExtruderId());
else
for (int extruder_id : bindings)
add_to_extruder(item.second, extruder_id);
continue;
}
int extruder_id;
// With the switch ready every AMS feeds both extruders, so attribute each to the extruder its
// input track feeds. Without a switch the binding is a single extruder
@@ -2722,20 +2979,13 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
extruder_id = uniq_extruder_id.value();
}
if (extruder_id >= machine_extruder_infos.size())
continue;
if (item.second->GetAmsType() == DevAms::N3S)
{ // N3S
machine_extruder_infos[extruder_id].ams_1++;
machine_extruder_infos[extruder_id].ams_v1.push_back(item.second);
} else {
machine_extruder_infos[extruder_id].ams_4++;
machine_extruder_infos[extruder_id].ams_v4.push_back(item.second);
}
add_to_extruder(item.second, extruder_id);
}
std::reverse(machine_extruder_infos.begin(), machine_extruder_infos.end());
// UI left is the deputy extruder on Bambu's dual-tool cards, hence the reversal. Generic
// multi-tool cards keep the tool order the printer reports.
if (!generic_multi_tool)
std::reverse(machine_extruder_infos.begin(), machine_extruder_infos.end());
std::vector<bool> extruder_synced(extruder_nums, false);
if (extruder_nums == 1) {
@@ -2772,6 +3022,16 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
right_extruder->sync_ams(obj, {}, {});
}
}
else if (generic_multi_tool) {
// Show the selected tool's AMS previews and badge. The other tools keep their sync state for
// the status icon but have no card of their own.
for (size_t i = 0; i < extruder_infos.size(); ++i)
extruder_synced[i] = is_same_nozzle_info(extruder_infos[i], machine_extruder_infos[i]);
const size_t tool = size_t(std::clamp(selected_tool, 0, extruder_nums - 1));
single_extruder->ShowBadge(extruder_synced[tool]);
single_extruder->sync_ams(obj, machine_extruder_infos[tool].ams_v4, machine_extruder_infos[tool].ams_v1, true);
}
StateColor synced_colour(std::pair<wxColour, int>(wxColour("#CECECE"), StateColor::Normal));
bool all_extruder_synced = std::all_of(extruder_synced.begin(), extruder_synced.end(), [](bool value) { return value; });
@@ -4100,7 +4360,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
select = combo_flow->GetCount() - 1;
combo_flow->SetSelection(select);
// No flow variant for this extruder: the diameter keeps the whole row.
combo_flow->Show(combo_flow->GetCount() > 0);
combo_flow->Show(combo_flow->GetCount() > 0 && extruder.IsNozzleRowVisible(row));
};
auto update_extruder_diameter = [&diameters, &nozzle_diameter, &diameter](int extruder_index,ExtruderGroup & extruder) {
@@ -4147,7 +4407,10 @@ void Sidebar::update_presets(Preset::Type preset_type)
//}
p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, PRINTER_THUMBNAIL_SIZE.GetHeight()));
} else {
AMSCountPopupWindow::UpdateAMSCount(0, p->single_extruder);
// The generic multi-tool card is populated from the live AMS bindings, not the two-entry
// AMS-count model used by the editable left/right cards.
if (!p->generic_multi_tool_layout)
AMSCountPopupWindow::UpdateAMSCount(0, p->single_extruder);
for (size_t row = 0; row < p->single_extruder->NozzleRowCount(); ++row)
update_extruder_variant(*p->single_extruder, row, int(row));
//if (!p->is_switching_diameter)