From 7688f7a16cb90cf4c160131c3a9699969d59d892 Mon Sep 17 00:00:00 2001 From: KrishManan Date: Sat, 30 May 2026 17:44:24 +0800 Subject: [PATCH 1/4] Add dropdown item action icons --- src/slic3r/GUI/Widgets/ComboBox.cpp | 37 ++++++++++++- src/slic3r/GUI/Widgets/ComboBox.hpp | 2 + src/slic3r/GUI/Widgets/DropDown.cpp | 82 +++++++++++++++++++++++++++-- src/slic3r/GUI/Widgets/DropDown.hpp | 4 ++ 4 files changed, 119 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 783e1caadf..b0d3746ad2 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -69,6 +69,11 @@ ComboBox::ComboBox(wxWindow *parent, e.SetId(GetId()); GetEventHandler()->ProcessEvent(e); }); + drop.Bind(EVT_DROPDOWN_ITEM_ACTION, [this](wxCommandEvent &e) { + e.SetEventObject(this); + e.SetId(GetId()); + GetEventHandler()->ProcessEvent(e); + }); drop.Bind(EVT_DISMISS, [this](auto &) { drop_down = false; wxCommandEvent e(wxEVT_COMBOBOX_CLOSEUP); @@ -222,7 +227,7 @@ int ComboBox::Append(const wxString &text, int style) { auto valid_bit_map = (&bitmap && bitmap.IsOk()) ? bitmap : wxNullBitmap; - Item item{text, wxEmptyString, valid_bit_map, valid_bit_map, clientData, group_key, group_label}; + Item item{text, wxEmptyString, valid_bit_map, valid_bit_map, wxNullBitmap, clientData, group_key, group_label}; item.style = style; items.push_back(item); SetClientDataType(wxClientData_Void); @@ -268,6 +273,27 @@ void ComboBox::ForceDropdownOpen() } } +void ComboBox::RefreshDropdownIfOpen() +{ + if (!drop_down) + return; + + drop.hover_item = -1; + drop.offset = wxPoint(); + if (drop.subDropDown) { + drop.subDropDown->group.clear(); + drop.subDropDown->hover_item = -1; + drop.subDropDown->offset = wxPoint(); + if (drop.subDropDown->IsShown()) + drop.subDropDown->Dismiss(); + } + + drop.need_sync = true; + drop.messureSize(); + drop.autoPosition(); + drop.paintNow(); +} + unsigned int ComboBox::GetCount() const { return items.size(); } void ComboBox::set_replace_text(wxString text, wxString image_name) @@ -321,6 +347,13 @@ void ComboBox::SetItemBitmap(unsigned int n, wxBitmap const &bitmap) drop.Invalidate(); } +void ComboBox::SetItemActionBitmap(unsigned int n, wxBitmap const &bitmap) +{ + if (n >= items.size()) return; + items[n].action_icon = (&bitmap && bitmap.IsOk()) ? bitmap : wxNullBitmap; + drop.Invalidate(); +} + int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items, unsigned int pos, void ** clientData, @@ -328,7 +361,7 @@ int ComboBox::DoInsertItems(const wxArrayStringsAdapter &items, { if (pos > this->items.size()) return -1; for (int i = 0; i < items.GetCount(); ++i) { - Item item { items[i], wxEmptyString, wxNullBitmap, wxNullBitmap, clientData ? clientData[i] : NULL }; + Item item { items[i], wxEmptyString, wxNullBitmap, wxNullBitmap, wxNullBitmap, clientData ? clientData[i] : NULL }; this->items.insert(this->items.begin() + pos, item); ++pos; } diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp index 552909b477..241458a7dd 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.hpp +++ b/src/slic3r/GUI/Widgets/ComboBox.hpp @@ -80,10 +80,12 @@ public: wxBitmap GetItemBitmap(unsigned int n); void SetItemBitmap(unsigned int n, wxBitmap const &bitmap); + void SetItemActionBitmap(unsigned int n, wxBitmap const &bitmap); bool is_drop_down(){return drop_down;} void DeleteOneItem(unsigned int pos) { DoDeleteOneItem(pos); } void ForceDropdownOpen(); + void RefreshDropdownIfOpen(); protected: virtual int DoInsertItems(const wxArrayStringsAdapter &items, diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 973113d0ac..7cb9aa6709 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -13,6 +13,7 @@ #include wxDEFINE_EVENT(EVT_DISMISS, wxCommandEvent); +wxDEFINE_EVENT(EVT_DROPDOWN_ITEM_ACTION, wxCommandEvent); BEGIN_EVENT_TABLE(DropDown, PopupWindow) @@ -225,6 +226,29 @@ static wxSize GetBmpSize(wxBitmap & bmp) #endif } +wxSize DropDown::actionIconSize() const +{ + wxSize size; + std::set groups; + for (const Item& item : items) { + if (group.IsEmpty()) { + if (!item.group_key.IsEmpty()) { + if (groups.find(item.group_key) != groups.end()) + continue; + groups.insert(item.group_key); + } + } else if (item.group_key != group) { + continue; + } + + wxBitmap icon = item.action_icon; + wxSize icon_size = GetBmpSize(icon); + size.x = std::max(size.x, icon_size.x); + size.y = std::max(size.y, icon_size.y); + } + return size; +} + static void _DrawSplitItem(const wxWindow* w, wxDC& dc, wxString split_text, wxPoint start_pt, int item_width, int item_height) { // save dc @@ -354,6 +378,9 @@ void DropDown::render(wxDC &dc) rcContent.width -= szBmp.x + 5; } + const wxSize action_icon_size = actionIconSize(); + const int action_column_width = action_icon_size.x > 0 ? action_icon_size.x + FromDIP(16) : 0; + std::set groups; // draw texts & icons int index = 0; @@ -392,6 +419,7 @@ void DropDown::render(wxDC &dc) } if (rcContent.y > size.y) break; wxPoint pt = rcContent.GetLeftTop(); + int rcContent_width = rcContent.width; if (item.style & DD_ITEM_STYLE_SPLIT_ITEM) { _DrawSplitItem(this, dc, item.text, pt, rowSize.GetWidth(), rowSize.GetHeight()); @@ -417,13 +445,27 @@ void DropDown::render(wxDC &dc) auto text = group.IsEmpty() ? (item.group_key.IsEmpty() ? item.text : item.group_label) : item.text; + if (action_column_width > 0) + rcContent.width -= action_column_width; + + if (item.action_icon.IsOk()) { + auto action_size = GetBmpSize(item.action_icon); + wxPoint action_pt(rcContent.x + rcContent_width - action_size.x - FromDIP(8), + rcContent.y + (rcContent.height - action_size.y) / 2); + dc.DrawBitmap(item.action_icon, action_pt); + } + if (!text_off && !text.IsEmpty()) { wxSize tSize = dc.GetMultiLineTextExtent(text); if (pt.x + tSize.x > rcContent.GetRight()) { - if (is_hover && item.tip.IsEmpty()) - SetToolTip(text); - text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, - rcContent.GetRight() - pt.x); + int max_text_width = rcContent.GetRight() - pt.x; + if (max_text_width <= 0) { + text.clear(); + } else { + if (is_hover && item.tip.IsEmpty()) + SetToolTip(text); + text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, max_text_width); + } } pt.y += (rcContent.height - textSize.y) / 2; dc.SetFont(GetFont()); @@ -436,6 +478,7 @@ void DropDown::render(wxDC &dc) dc.DrawBitmap(arrow_bitmap.bmp(), pt); } } + rcContent.width = rcContent_width; rcContent.y += rowSize.y; } } @@ -545,6 +588,8 @@ void DropDown::messureSize() size1.x += size2.x + (text_off ? 0 : 5); } } + if (item.action_icon.IsOk()) + size1.x += GetBmpSize(item.action_icon).x + FromDIP(16); if (size1.x > textSize.x) textSize = size1; } if (!align_icon) iconSize.x = 0; @@ -616,6 +661,11 @@ void DropDown::messureSize() e.SetId(GetId()); GetEventHandler()->ProcessEvent(e); }); + subDropDown->Bind(EVT_DROPDOWN_ITEM_ACTION, [this](wxCommandEvent &e) { + e.SetEventObject(this); + e.SetId(GetId()); + GetEventHandler()->ProcessEvent(e); + }); } need_sync = false; } @@ -694,6 +744,19 @@ void DropDown::mouseReleased(wxMouseEvent& event) if (hover_item < 0) return; if (hover_item >= 0 && (subDropDown == nullptr || subDropDown->group.empty())) { // not moved + int index = hoverIndex(); + if (index >= 0 && items[index].action_icon.IsOk()) { + wxSize action_size = actionIconSize(); + int action_right = GetSize().x; + if (rowSize.y * count > GetSize().y) + action_right -= 6; + int action_left = action_right - action_size.x - FromDIP(16); + if (event.GetX() >= action_left && event.GetX() <= action_right) { + sendDropDownActionEvent(index); + paintNow(); + return; + } + } sendDropDownEvent(); if (mainDropDown) mainDropDown->hover_item = -1; // To Dismiss mainDropDown @@ -810,6 +873,17 @@ void DropDown::sendDropDownEvent() GetEventHandler()->ProcessEvent(event); } +void DropDown::sendDropDownActionEvent(int index) +{ + if (index < 0 || index >= int(items.size()) || (items[index].style & DD_ITEM_STYLE_DISABLED)) + return; + wxCommandEvent event(EVT_DROPDOWN_ITEM_ACTION, GetId()); + event.SetEventObject(this); + event.SetInt(index); + event.SetString(items[index].text); + GetEventHandler()->ProcessEvent(event); +} + void DropDown::Dismiss() { if (subDropDown && subDropDown->IsShown()) diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index 09041e3dc0..a2ad873b94 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -15,6 +15,7 @@ #define DD_ITEM_STYLE_DISABLED 0x0002 // ----text----, text with horizontal line arounds wxDECLARE_EVENT(EVT_DISMISS, wxCommandEvent); +wxDECLARE_EVENT(EVT_DROPDOWN_ITEM_ACTION, wxCommandEvent); class DropDown : public PopupWindow { @@ -25,6 +26,7 @@ public: wxString text_static_tips;// display static tips for TextInput.eg.PrinterInfoBox wxBitmap icon; wxBitmap icon_textctrl;// display icon for TextInput.eg.PrinterInfoBox + wxBitmap action_icon; void * data{nullptr}; wxString group_key{}; wxString group_label{}; @@ -123,6 +125,7 @@ private: int hoverIndex(); int selectedItem(); + wxSize actionIconSize() const; friend class ComboBox; void messureSize(); @@ -136,6 +139,7 @@ private: void mouseWheelMoved(wxMouseEvent &event); void sendDropDownEvent(); + void sendDropDownActionEvent(int index); DECLARE_EVENT_TABLE() From fb6c93963dae3f13bfec4859d9804f267f80af42 Mon Sep 17 00:00:00 2001 From: KrishManan Date: Sat, 30 May 2026 17:44:51 +0800 Subject: [PATCH 2/4] Add AMS filament favorites --- src/slic3r/GUI/AMSMaterialsSetting.cpp | 201 +++++++++++++++++++------ src/slic3r/GUI/AMSMaterialsSetting.hpp | 17 ++- 2 files changed, 170 insertions(+), 48 deletions(-) diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 4a429a4a5a..f5aa15df94 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -2,6 +2,7 @@ #include "ExtrusionCalibration.hpp" #include "MsgDialog.hpp" #include "GUI_App.hpp" +#include "libslic3r/Config.hpp" #include "libslic3r/Preset.hpp" #include "I18N.hpp" #include @@ -116,7 +117,8 @@ void AMSMaterialsSetting::create() Bind(wxEVT_PAINT, &AMSMaterialsSetting::paintEvent, this); Bind(EVT_SELECTED_COLOR, &AMSMaterialsSetting::on_picker_color, this); - m_comboBox_filament->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this); + m_comboBox_filament->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this); + m_comboBox_filament->Bind(EVT_DROPDOWN_ITEM_ACTION, &AMSMaterialsSetting::on_toggle_favorite_filament, this); m_comboBox_cali_result->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_cali_result), NULL, this); } @@ -135,11 +137,7 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_sizer_filament->Add(0, 0, 0, wxEXPAND, 0); -#ifdef __APPLE__ - m_comboBox_filament = new wxComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY); -#else m_comboBox_filament = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY); -#endif m_sizer_filament->Add(m_comboBox_filament, 1, wxALIGN_CENTER, 0); @@ -371,6 +369,7 @@ void AMSMaterialsSetting::paintEvent(wxPaintEvent &evt) AMSMaterialsSetting::~AMSMaterialsSetting() { m_comboBox_filament->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this); + m_comboBox_filament->Unbind(EVT_DROPDOWN_ITEM_ACTION, &AMSMaterialsSetting::on_toggle_favorite_filament, this); m_comboBox_cali_result->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_cali_result), NULL, this); } @@ -472,10 +471,9 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) { std::string selected_ams_id; PresetBundle *preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { + const FilamentInfos* selected_info = selected_filament_info(); for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; - std::string filament_id = filament_item.filament_id; - if (it->filament_id.compare(filament_id) == 0) { + if (selected_info && it->filament_id.compare(selected_info->filament_id) == 0) { selected_ams_id = it->filament_id; break; } @@ -539,11 +537,9 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { + const FilamentInfos* selected_info = selected_filament_info(); for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - - auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; - std::string filament_id = filament_item.filament_id; - if (it->filament_id.compare(filament_id) == 0) { + if (selected_info && it->filament_id.compare(selected_info->filament_id) == 0) { //check is it in the filament blacklist @@ -840,6 +836,136 @@ static void _collect_filament_info(const wxString& shown_name, query_filament_types[shown_name] = filament.config.get_filament_type(); } +static const char* AMS_FAVORITE_FILAMENT_IDS_KEY = "ams_favorite_filament_ids"; + +const AMSMaterialsSetting::FilamentInfos* AMSMaterialsSetting::selected_filament_info() const +{ + int selection = m_comboBox_filament->GetSelection(); + if (selection >= 0 && selection < int(m_filament_list_items.size()) && m_filament_list_items[selection].is_filament) + return &m_filament_list_items[selection].filament; + + auto iter = map_filament_items.find(m_comboBox_filament->GetValue().ToStdString()); + return iter == map_filament_items.end() ? nullptr : &iter->second; +} + +std::set AMSMaterialsSetting::load_favorite_filament_ids() const +{ + std::set favorite_ids; + std::vector values; + const std::string serialized = wxGetApp().app_config->get(AMS_FAVORITE_FILAMENT_IDS_KEY); + if (!serialized.empty() && Slic3r::unescape_strings_cstyle(serialized, values)) { + favorite_ids.insert(values.begin(), values.end()); + } + return favorite_ids; +} + +void AMSMaterialsSetting::save_favorite_filament_ids(const std::set& favorite_ids) +{ + std::vector values(favorite_ids.begin(), favorite_ids.end()); + wxGetApp().app_config->set(AMS_FAVORITE_FILAMENT_IDS_KEY, Slic3r::escape_strings_cstyle(values)); + wxGetApp().app_config->save(); +} + +void AMSMaterialsSetting::rebuild_filament_dropdown_items(const std::string& selected_filament_id) +{ + std::vector items; + m_filament_list_items.clear(); + + const std::set favorite_ids = load_favorite_filament_ids(); + wxBitmap star_filled = ScalableBitmap(this, "score_star_light", 16).bmp(); + wxBitmap star_empty = ScalableBitmap(this, "score_star_dark", 16).bmp(); + + auto append_header = [&items, this](const wxString& label) { + DropDown::Item item; + item.text = label; + item.style = DD_ITEM_STYLE_SPLIT_ITEM | DD_ITEM_STYLE_DISABLED; + items.push_back(item); + m_filament_list_items.push_back(FilamentListItem{}); + }; + + auto append_filament = [&items, this, &favorite_ids, &star_filled, &star_empty](const wxString& name) { + auto info_it = map_filament_items.find(name.ToStdString()); + if (info_it == map_filament_items.end()) + return; + + const bool is_favorite = favorite_ids.find(info_it->second.filament_id) != favorite_ids.end(); + DropDown::Item item; + item.text = name; + item.action_icon = is_favorite ? star_filled : star_empty; + item.style = 0; + items.push_back(item); + + FilamentListItem list_item; + list_item.filament = info_it->second; + list_item.is_filament = true; + m_filament_list_items.push_back(list_item); + }; + + bool has_favorites = false; + for (const wxString& name : m_sorted_filament_items) { + auto info_it = map_filament_items.find(name.ToStdString()); + if (info_it != map_filament_items.end() && favorite_ids.find(info_it->second.filament_id) != favorite_ids.end()) { + has_favorites = true; + break; + } + } + + if (has_favorites) { + append_header(_L("Favorite filaments")); + for (const wxString& name : m_sorted_filament_items) { + auto info_it = map_filament_items.find(name.ToStdString()); + if (info_it != map_filament_items.end() && favorite_ids.find(info_it->second.filament_id) != favorite_ids.end()) + append_filament(name); + } + } + + append_header(_L("All filaments")); + for (const wxString& name : m_sorted_filament_items) + append_filament(name); + + int selection_idx = -1; + if (!selected_filament_id.empty()) { + for (int i = 0; i < int(m_filament_list_items.size()); ++i) { + if (m_filament_list_items[i].is_filament && m_filament_list_items[i].filament.filament_id == selected_filament_id) { + selection_idx = i; + break; + } + } + } + + m_comboBox_filament->SetItems(items); + m_comboBox_filament->SetSelection(selection_idx); +} + +void AMSMaterialsSetting::toggle_favorite_filament(const std::string& filament_id) +{ + if (filament_id.empty()) + return; + + std::set favorite_ids = load_favorite_filament_ids(); + auto iter = favorite_ids.find(filament_id); + if (iter == favorite_ids.end()) + favorite_ids.insert(filament_id); + else + favorite_ids.erase(iter); + + save_favorite_filament_ids(favorite_ids); +} + +void AMSMaterialsSetting::on_toggle_favorite_filament(wxCommandEvent& evt) +{ + const int index = evt.GetInt(); + if (index < 0 || index >= int(m_filament_list_items.size()) || !m_filament_list_items[index].is_filament) + return; + + std::string selected_filament_id; + if (const FilamentInfos* info = selected_filament_info()) + selected_filament_id = info->filament_id; + toggle_favorite_filament(m_filament_list_items[index].filament.filament_id); + rebuild_filament_dropdown_items(selected_filament_id); + m_comboBox_filament->RefreshDropdownIfOpen(); +} + void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_min, wxString temp_max, wxString k, wxString n) { if (!obj) return; @@ -855,6 +981,9 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi int idx = 0; wxArrayString filament_items; + map_filament_items.clear(); + m_sorted_filament_items.clear(); + m_filament_list_items.clear(); wxString bambu_filament_name; wxString hint_filament_name; // the hint type to be selected std::unordered_map query_filament_vendors;// some information for sort @@ -1050,21 +1179,18 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi std::sort(filament_items.begin(), filament_items.end(), _filament_sorter); } - // traverse the hint selection idx - int selection_idx = -1; - { - for(int i = 0; i < filament_items.size(); i++) - { - if (hint_filament_name == filament_items[i]) - { - selection_idx = i; - break; - } + std::string selection_filament_id; + for (int i = 0; i < int(filament_items.size()); ++i) { + m_sorted_filament_items.push_back(filament_items[i]); + if (hint_filament_name == filament_items[i]) { + auto info_it = map_filament_items.find(filament_items[i].ToStdString()); + if (info_it != map_filament_items.end()) + selection_filament_id = info_it->second.filament_id; } } - m_comboBox_filament->Set(filament_items); - m_comboBox_filament->SetSelection(selection_idx); + rebuild_filament_dropdown_items(selection_filament_id); + int selection_idx = m_comboBox_filament->GetSelection(); post_select_event(selection_idx); if (selection_idx < 0) { @@ -1106,6 +1232,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) int* from_printer = static_cast(m_comboBox_filament->GetClientData()); m_filament_type = ""; + const FilamentInfos* selected_info = selected_filament_info(); PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { std::ostringstream stream; @@ -1124,10 +1251,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) std::set printer_names = preset_bundle->get_printer_names_by_printer_type_and_nozzle(DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str); for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - if (!m_comboBox_filament->GetValue().IsEmpty()) { - auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; - std::string filament_id = filament_item.filament_id; - if (it->filament_id.compare(filament_id) == 0) { + if (selected_info) { + if (it->filament_id.compare(selected_info->filament_id) == 0) { ConfigOption * printer_opt = it->config.option("compatible_printers"); ConfigOptionStrings *printer_strs = dynamic_cast(printer_opt); bool has_compatible_printer = false; @@ -1186,7 +1311,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) //reset cali int cali_select_idx = -1; - if ( !this->obj || m_filament_selection < 0) { + if ( !this->obj || m_filament_selection < 0 || !selected_info) { m_input_k_val->Enable(false); m_input_n_val->Enable(false); m_button_confirm->Disable(); // ORCA No need to change style @@ -1205,21 +1330,9 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) ams_filament_id = ""; ams_setting_id = ""; - if (preset_bundle) { - for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - auto itor = map_filament_items.find(m_comboBox_filament->GetValue().ToStdString()); - if ( itor != map_filament_items.end()) { - ams_filament_id = itor->second.filament_id; - ams_setting_id = itor->second.setting_id; - break; - } - - if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) { - ams_filament_id = it->filament_id; - ams_setting_id = it->setting_id; - break; - } - } + if (selected_info) { + ams_filament_id = selected_info->filament_id; + ams_setting_id = selected_info->setting_id; } wxArrayString items; diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index 84688896e6..f00e55d0dc 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -139,6 +139,7 @@ protected: void create_panel_kn(wxWindow* parent); void on_dpi_changed(const wxRect &suggested_rect) override; void on_select_filament(wxCommandEvent& evt); + void on_toggle_favorite_filament(wxCommandEvent& evt); void on_select_cali_result(wxCommandEvent &evt); void on_select_ok(wxCommandEvent &event); void on_select_reset(wxCommandEvent &event); @@ -146,6 +147,10 @@ protected: void on_clr_picker(wxMouseEvent &event); bool is_virtual_tray(); void update_widgets(); + void rebuild_filament_dropdown_items(const std::string& selected_filament_id); + std::set load_favorite_filament_ids() const; + void save_favorite_filament_ids(const std::set& favorite_ids); + void toggle_favorite_filament(const std::string& filament_id); void update_filament_editing(bool is_printing); @@ -183,11 +188,7 @@ protected: int m_pa_cali_select_id = 0; -#ifdef __APPLE__ - wxComboBox *m_comboBox_filament; -#else ComboBox *m_comboBox_filament; -#endif ComboBox * m_comboBox_cali_result; TextInput* m_readonly_filament; @@ -195,7 +196,15 @@ protected: std::string filament_id; std::string setting_id; }; + struct FilamentListItem { + FilamentInfos filament; + bool is_filament{false}; + }; + const FilamentInfos* selected_filament_info() const; + std::map map_filament_items; + std::vector m_sorted_filament_items; + std::vector m_filament_list_items; }; wxDECLARE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent); From 01b340dee16a327760211bb79cb4dc036a0d52bf Mon Sep 17 00:00:00 2001 From: SoftFever Date: Mon, 12 Oct 2026 14:54:17 +0800 Subject: [PATCH 3/4] Fix regressions in the AMS filament favorites dropdown --- src/slic3r/GUI/AMSMaterialsSetting.cpp | 126 +++++++++++++------------ src/slic3r/GUI/AMSMaterialsSetting.hpp | 8 +- src/slic3r/GUI/Widgets/ComboBox.cpp | 37 +++----- src/slic3r/GUI/Widgets/ComboBox.hpp | 2 +- src/slic3r/GUI/Widgets/DropDown.cpp | 56 +++++------ src/slic3r/GUI/Widgets/DropDown.hpp | 4 +- 6 files changed, 111 insertions(+), 122 deletions(-) diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 671431dd2f..b2b5f46466 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -16,6 +16,7 @@ #include #include "slic3r/GUI/GUI_Utils.hpp" #include "slic3r/GUI/Widgets/Button.hpp" +#include "slic3r/GUI/Widgets/DropDown.hpp" #include "slic3r/GUI/Widgets/Label.hpp" #include #include "slic3r/GUI/wxExtensions.hpp" @@ -175,6 +176,24 @@ void AMSMaterialsSetting::create() m_comboBox_filament->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this); m_comboBox_filament->Bind(EVT_DROPDOWN_ITEM_ACTION, &AMSMaterialsSetting::on_toggle_favorite_filament, this); + // Favorites toggled in the open list are regrouped just before it opens again, so rows never move + // under the cursor. These handlers run ahead of ComboBox's own, which then measure and show the + // rebuilt rows; wxEVT_COMBOBOX_DROPDOWN would be too late, as ComboBox sends it after showing the list. + auto regroup_favorites = [this]() { + if (!m_favorites_regroup || m_comboBox_filament->is_drop_down()) + return; + const FilamentInfos* info = selected_filament_info(); + const std::string filament_id = info ? info->filament_id : std::string(); + rebuild_filament_dropdown_items(filament_id); + }; + m_comboBox_filament->Bind(wxEVT_LEFT_DOWN, [regroup_favorites](wxMouseEvent& e) { regroup_favorites(); e.Skip(); }); + m_comboBox_filament->Bind(wxEVT_LEFT_DCLICK, [regroup_favorites](wxMouseEvent& e) { regroup_favorites(); e.Skip(); }); + m_comboBox_filament->Bind(wxEVT_KEY_DOWN, [regroup_favorites](wxKeyEvent& e) { + if (e.GetKeyCode() == WXK_RETURN || e.GetKeyCode() == WXK_SPACE) // the keys that open the list + regroup_favorites(); + e.Skip(); + }); + m_comboBox_cali_result->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_cali_result), NULL, this); } @@ -193,6 +212,8 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_sizer_filament->Add(0, 0, 0, wxEXPAND, 0); m_comboBox_filament = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY); + m_star_filled = ScalableBitmap(this, "score_star_light", 16); + m_star_empty = ScalableBitmap(this, "score_star_dark", 16); m_sizer_filament->Add(m_comboBox_filament, 1, wxALIGN_CENTER, 0); @@ -988,12 +1009,10 @@ static const char* AMS_FAVORITE_FILAMENT_IDS_KEY = "ams_favorite_filament_ids"; const AMSMaterialsSetting::FilamentInfos* AMSMaterialsSetting::selected_filament_info() const { - int selection = m_comboBox_filament->GetSelection(); - if (selection >= 0 && selection < int(m_filament_list_items.size()) && m_filament_list_items[selection].is_filament) - return &m_filament_list_items[selection].filament; - - auto iter = map_filament_items.find(m_comboBox_filament->GetValue().ToStdString()); - return iter == map_filament_items.end() ? nullptr : &iter->second; + const int selection = m_comboBox_filament->GetSelection(); + if (selection < 0 || selection >= int(m_filament_list_items.size()) || !m_filament_list_items[selection].is_filament) + return nullptr; + return &m_filament_list_items[selection].filament; } std::set AMSMaterialsSetting::load_favorite_filament_ids() const @@ -1019,10 +1038,6 @@ void AMSMaterialsSetting::rebuild_filament_dropdown_items(const std::string& sel std::vector items; m_filament_list_items.clear(); - const std::set favorite_ids = load_favorite_filament_ids(); - wxBitmap star_filled = ScalableBitmap(this, "score_star_light", 16).bmp(); - wxBitmap star_empty = ScalableBitmap(this, "score_star_dark", 16).bmp(); - auto append_header = [&items, this](const wxString& label) { DropDown::Item item; item.text = label; @@ -1031,43 +1046,32 @@ void AMSMaterialsSetting::rebuild_filament_dropdown_items(const std::string& sel m_filament_list_items.push_back(FilamentListItem{}); }; - auto append_filament = [&items, this, &favorite_ids, &star_filled, &star_empty](const wxString& name) { - auto info_it = map_filament_items.find(name.ToStdString()); + auto append_filament = [&items, this](const wxString& name) { + auto info_it = map_filament_items.find(name); if (info_it == map_filament_items.end()) return; - const bool is_favorite = favorite_ids.find(info_it->second.filament_id) != favorite_ids.end(); DropDown::Item item; item.text = name; - item.action_icon = is_favorite ? star_filled : star_empty; - item.style = 0; items.push_back(item); - - FilamentListItem list_item; - list_item.filament = info_it->second; - list_item.is_filament = true; - m_filament_list_items.push_back(list_item); + m_filament_list_items.push_back(FilamentListItem{info_it->second, true}); }; - bool has_favorites = false; + // Favorites are listed again at the top under their own header. Without any, the list is the + // plain filament list. + const std::set favorite_ids = load_favorite_filament_ids(); + std::vector favorites; for (const wxString& name : m_sorted_filament_items) { - auto info_it = map_filament_items.find(name.ToStdString()); - if (info_it != map_filament_items.end() && favorite_ids.find(info_it->second.filament_id) != favorite_ids.end()) { - has_favorites = true; - break; - } + auto info_it = map_filament_items.find(name); + if (info_it != map_filament_items.end() && favorite_ids.count(info_it->second.filament_id) > 0) + favorites.push_back(name); } - - if (has_favorites) { + if (!favorites.empty()) { append_header(_L("Favorite filaments")); - for (const wxString& name : m_sorted_filament_items) { - auto info_it = map_filament_items.find(name.ToStdString()); - if (info_it != map_filament_items.end() && favorite_ids.find(info_it->second.filament_id) != favorite_ids.end()) - append_filament(name); - } + for (const wxString& name : favorites) + append_filament(name); + append_header(_L("All filaments")); } - - append_header(_L("All filaments")); for (const wxString& name : m_sorted_filament_items) append_filament(name); @@ -1081,8 +1085,25 @@ void AMSMaterialsSetting::rebuild_filament_dropdown_items(const std::string& sel } } + // Clear() first: SetItems() keeps the old selection, and SetSelection() ignores an index equal to it, + // which would leave the previous filament's name on the combo. + m_comboBox_filament->Clear(); m_comboBox_filament->SetItems(items); + update_favorite_stars(); m_comboBox_filament->SetSelection(selection_idx); + m_favorites_regroup = false; +} + +void AMSMaterialsSetting::update_favorite_stars() +{ + const std::set favorite_ids = load_favorite_filament_ids(); + for (size_t i = 0; i < m_filament_list_items.size(); ++i) { + const FilamentListItem& row = m_filament_list_items[i]; + if (row.is_filament) { + const bool favorite = favorite_ids.count(row.filament.filament_id) > 0; + m_comboBox_filament->SetItemActionBitmap(i, favorite ? m_star_filled.bmp() : m_star_empty.bmp()); + } + } } void AMSMaterialsSetting::toggle_favorite_filament(const std::string& filament_id) @@ -1106,12 +1127,9 @@ void AMSMaterialsSetting::on_toggle_favorite_filament(wxCommandEvent& evt) if (index < 0 || index >= int(m_filament_list_items.size()) || !m_filament_list_items[index].is_filament) return; - std::string selected_filament_id; - if (const FilamentInfos* info = selected_filament_info()) - selected_filament_id = info->filament_id; toggle_favorite_filament(m_filament_list_items[index].filament.filament_id); - rebuild_filament_dropdown_items(selected_filament_id); - m_comboBox_filament->RefreshDropdownIfOpen(); + update_favorite_stars(); + m_favorites_regroup = true; } void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_min, wxString temp_max, wxString k, wxString n) @@ -1127,16 +1145,12 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi m_input_k_val->GetTextCtrl()->SetValue(k); m_input_n_val->GetTextCtrl()->SetValue(n); - wxArrayString filament_items; - map_filament_items.clear(); - m_sorted_filament_items.clear(); - m_filament_list_items.clear(); wxString bambu_filament_name; - wxString hint_filament_name; // the hint type to be selected std::unordered_map query_filament_vendors;// some information for sort std::unordered_map query_filament_types; // std::set filament_id_set; + m_sorted_filament_items.clear(); // The alias keyed map has to start empty: it is a member, so a stale alias left by an earlier // popup (a different printer, a different nozzle) would resolve to that printer's filament_id. map_filament_items.clear(); @@ -1166,7 +1180,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi if (alias.empty()) continue; - filament_items.push_back(alias); + m_sorted_filament_items.push_back(alias); _collect_filament_info(alias, *filament_it, query_filament_vendors, query_filament_types); FilamentInfos filament_infos; @@ -1175,7 +1189,6 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi map_filament_items[alias] = filament_infos; if (filament_it->filament_id == ams_filament_id) { - hint_filament_name = from_u8(alias); bambu_filament_name = from_u8(alias); @@ -1346,20 +1359,10 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi return name_compare != 0 ? name_compare < 0 : left < right; }; - std::sort(filament_items.begin(), filament_items.end(), _filament_sorter); + std::sort(m_sorted_filament_items.begin(), m_sorted_filament_items.end(), _filament_sorter); } - std::string selection_filament_id; - for (int i = 0; i < int(filament_items.size()); ++i) { - m_sorted_filament_items.push_back(filament_items[i]); - if (hint_filament_name == filament_items[i]) { - auto info_it = map_filament_items.find(filament_items[i].ToStdString()); - if (info_it != map_filament_items.end()) - selection_filament_id = info_it->second.filament_id; - } - } - - rebuild_filament_dropdown_items(selection_filament_id); + rebuild_filament_dropdown_items(ams_filament_id); int selection_idx = m_comboBox_filament->GetSelection(); post_select_event(selection_idx); @@ -1645,6 +1648,11 @@ void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect) m_button_reset->Rescale(); // ORCA m_button_confirm->Rescale(); // ORCA m_button_close->Rescale(); // ORCA + // An open list would keep the measurements of the old stars, so close it before replacing them. + m_comboBox_filament->CloseDropdown(); + m_star_filled.msw_rescale(); + m_star_empty.msw_rescale(); + update_favorite_stars(); this->Refresh(); } diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index 673ead26ea..6fa215063a 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -166,6 +166,7 @@ protected: bool is_virtual_tray(); void update_widgets(); void rebuild_filament_dropdown_items(const std::string& selected_filament_id); + void update_favorite_stars(); std::set load_favorite_filament_ids() const; void save_favorite_filament_ids(const std::set& favorite_ids); void toggle_favorite_filament(const std::string& filament_id); @@ -222,9 +223,14 @@ protected: }; const FilamentInfos* selected_filament_info() const; - std::map map_filament_items; + // Keyed by the same wxString the dropdown shows, so a row's name never has to be converted back. + std::map map_filament_items; std::vector m_sorted_filament_items; std::vector m_filament_list_items; + ScalableBitmap m_star_filled; + ScalableBitmap m_star_empty; + // Favorites changed while the list was open; their rows are regrouped before it opens again. + bool m_favorites_regroup{false}; }; wxDECLARE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent); diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 500b8c364a..55e261d04d 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -302,25 +302,10 @@ void ComboBox::ForceDropdownOpen() } } -void ComboBox::RefreshDropdownIfOpen() +void ComboBox::CloseDropdown() { - if (!drop_down) - return; - - drop.hover_item = -1; - drop.offset = wxPoint(); - if (drop.subDropDown) { - drop.subDropDown->group.clear(); - drop.subDropDown->hover_item = -1; - drop.subDropDown->offset = wxPoint(); - if (drop.subDropDown->IsShown()) - drop.subDropDown->Dismiss(); - } - - drop.need_sync = true; - drop.messureSize(); - drop.autoPosition(); - drop.paintNow(); + if (drop.IsShown()) + drop.DismissAndNotify(); } unsigned int ComboBox::GetCount() const { return items.size(); } @@ -452,16 +437,18 @@ void ComboBox::keyDown(wxKeyEvent& event) case WXK_UP: case WXK_DOWN: case WXK_LEFT: - case WXK_RIGHT: - if ((event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) && GetSelection() > 0) { - SetSelection(GetSelection() - 1); - } else if ((event.GetKeyCode() == WXK_DOWN || event.GetKeyCode() == WXK_RIGHT) && GetSelection() + 1 < items.size()) { - SetSelection(GetSelection() + 1); - } else { + case WXK_RIGHT: { + // Step over disabled rows: the mouse cannot select them either. + const int step = (event.GetKeyCode() == WXK_UP || event.GetKeyCode() == WXK_LEFT) ? -1 : 1; + int n = GetSelection() + step; + while (n >= 0 && n < int(items.size()) && (items[n].style & DD_ITEM_STYLE_DISABLED)) + n += step; + if (n < 0 || n >= int(items.size())) break; - } + SetSelection(n); sendComboBoxEvent(); break; + } case WXK_TAB: HandleAsNavigationKey(event); break; diff --git a/src/slic3r/GUI/Widgets/ComboBox.hpp b/src/slic3r/GUI/Widgets/ComboBox.hpp index 1ab04c3a79..1fbc2e257d 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.hpp +++ b/src/slic3r/GUI/Widgets/ComboBox.hpp @@ -101,7 +101,7 @@ public: void DeleteOneItem(unsigned int pos) { DoDeleteOneItem(pos); } void ForceDropdownOpen(); - void RefreshDropdownIfOpen(); + void CloseDropdown(); protected: virtual int DoInsertItems(const wxArrayStringsAdapter &items, diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index 3fb1607551..966a090526 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -243,27 +243,17 @@ static wxSize GetBmpSize(wxBitmap & bmp) #endif } -wxSize DropDown::actionIconSize() const +// The x-range [first, second) of the column at the right of the rows that holds the items' action +// icons, empty when no item has one. render() draws the icons in it and mouseReleased() hit-tests +// clicks against it, so the drawn icon and its clickable area always match. +std::pair DropDown::actionColumn() const { - wxSize size; - std::set groups; - for (const Item& item : items) { - if (group.IsEmpty()) { - if (!item.group_key.IsEmpty()) { - if (groups.find(item.group_key) != groups.end()) - continue; - groups.insert(item.group_key); - } - } else if (item.group_key != group) { - continue; - } - - wxBitmap icon = item.action_icon; - wxSize icon_size = GetBmpSize(icon); - size.x = std::max(size.x, icon_size.x); - size.y = std::max(size.y, icon_size.y); - } - return size; + if (actionIconWidth <= 0) + return {0, 0}; + int right = rowSize.x; + if (rowSize.y * count > GetSize().y) // position bar + right -= 6; + return {right - actionIconWidth - FromDIP(16), right}; } static void _DrawSplitItem(const wxWindow* w, wxDC& dc, wxString split_text, wxPoint start_pt, int item_width, int item_height) @@ -395,8 +385,9 @@ void DropDown::render(wxDC &dc) rcContent.width -= szBmp.x + 5; } - const wxSize action_icon_size = actionIconSize(); - const int action_column_width = action_icon_size.x > 0 ? action_icon_size.x + FromDIP(16) : 0; + const auto action_column = actionColumn(); + if (action_column.second > action_column.first) + rcContent.width = action_column.first - rcContent.x; std::set groups; // draw texts & icons @@ -436,7 +427,6 @@ void DropDown::render(wxDC &dc) } if (rcContent.y > size.y) break; wxPoint pt = rcContent.GetLeftTop(); - int rcContent_width = rcContent.width; if (item.style & DD_ITEM_STYLE_SPLIT_ITEM) { _DrawSplitItem(this, dc, item.text, pt, rowSize.GetWidth(), rowSize.GetHeight()); @@ -462,12 +452,9 @@ void DropDown::render(wxDC &dc) auto text = group.IsEmpty() ? (item.group_key.IsEmpty() ? item.text : item.group_label) : item.text; - if (action_column_width > 0) - rcContent.width -= action_column_width; - if (item.action_icon.IsOk()) { auto action_size = GetBmpSize(item.action_icon); - wxPoint action_pt(rcContent.x + rcContent_width - action_size.x - FromDIP(8), + wxPoint action_pt(action_column.second - action_size.x - FromDIP(8), rcContent.y + (rcContent.height - action_size.y) / 2); dc.DrawBitmap(item.action_icon, action_pt); } @@ -498,7 +485,6 @@ void DropDown::render(wxDC &dc) dc.DrawBitmap(arrow_bitmap.bmp(), pt); } } - rcContent.width = rcContent_width; rcContent.y += rowSize.y; } } @@ -572,6 +558,7 @@ void DropDown::messureSize() if (!need_sync) return; textSize = wxSize(); iconSize = wxSize(); + actionIconWidth = 0; count = 0; wxClientDC dc(GetParent() ? GetParent() : this); dc.SetFont(GetFont()); @@ -609,9 +596,12 @@ void DropDown::messureSize() } } if (item.action_icon.IsOk()) - size1.x += GetBmpSize(item.action_icon).x + FromDIP(16); + actionIconWidth = std::max(actionIconWidth, GetBmpSize(item.action_icon).x); if (size1.x > textSize.x) textSize = size1; } + // Every row leaves room for the action column, as render() and actionColumn() lay it out. + if (actionIconWidth > 0) + textSize.x += actionIconWidth + FromDIP(16); if (!align_icon) iconSize.x = 0; wxSize szContent = textSize; if (szContent.x < FromDIP(120) && !use_content_width) @@ -766,12 +756,8 @@ void DropDown::mouseReleased(wxMouseEvent& event) if (hover_item >= 0 && (subDropDown == nullptr || subDropDown->group.empty())) { // not moved int index = hoverIndex(); if (index >= 0 && items[index].action_icon.IsOk()) { - wxSize action_size = actionIconSize(); - int action_right = GetSize().x; - if (rowSize.y * count > GetSize().y) - action_right -= 6; - int action_left = action_right - action_size.x - FromDIP(16); - if (event.GetX() >= action_left && event.GetX() <= action_right) { + const auto action_column = actionColumn(); + if (event.GetX() >= action_column.first && event.GetX() < action_column.second) { sendDropDownActionEvent(index); paintNow(); return; diff --git a/src/slic3r/GUI/Widgets/DropDown.hpp b/src/slic3r/GUI/Widgets/DropDown.hpp index adbe0eb95c..a0821e6fff 100644 --- a/src/slic3r/GUI/Widgets/DropDown.hpp +++ b/src/slic3r/GUI/Widgets/DropDown.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "slic3r/GUI/Widgets/StateColor.hpp" #include @@ -67,6 +68,7 @@ private: wxSize textSize; wxSize iconSize; + int actionIconWidth = 0; wxSize rowSize; StateHandler state_handler; @@ -137,7 +139,7 @@ private: int hoverIndex(); int selectedItem(); - wxSize actionIconSize() const; + std::pair actionColumn() const; friend class ComboBox; void messureSize(); From 6a1a5d51e99fbe7cb51424a620e397c1b60f7637 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Mon, 12 Oct 2026 16:03:49 +0800 Subject: [PATCH 4/4] Show an outline star for filaments that are not favorites --- resources/images/score_star_outline.svg | 3 +++ src/slic3r/GUI/AMSMaterialsSetting.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 resources/images/score_star_outline.svg diff --git a/resources/images/score_star_outline.svg b/resources/images/score_star_outline.svg new file mode 100644 index 0000000000..eb52be9e82 --- /dev/null +++ b/resources/images/score_star_outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index b2b5f46466..41047dffd9 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -213,7 +213,7 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_comboBox_filament = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY); m_star_filled = ScalableBitmap(this, "score_star_light", 16); - m_star_empty = ScalableBitmap(this, "score_star_dark", 16); + m_star_empty = ScalableBitmap(this, "score_star_outline", 16); m_sizer_filament->Add(m_comboBox_filament, 1, wxALIGN_CENTER, 0);