Files
OrcaSlicer/src/slic3r/GUI/AMSMaterialsSetting.cpp
T
Hanif Koh 057d21bb53 Remove using namespace std from json_diff.hpp
The directive sat at global scope in a header that DeviceManager.hpp
includes, so most of the GUI compiled with all of std in the global
namespace. 42 files had come to rely on it, mostly for string, vector
and unordered_map, four of them for the ""sv and ""ms literals.

Those sites are qualified. GCodeViewer.cpp spelled the type as
std::vector<::string>, which only resolved through the directive. The
files that use the ""sv and ""ms literals get a file-scope
"using namespace std::string_view_literals;" or
"using namespace std::chrono_literals;", as other sources already do.
2026-10-07 14:55:09 +08:00

1983 lines
80 KiB
C++

#include "AMSMaterialsSetting.hpp"
#include "ExtrusionCalibration.hpp"
#include "MsgDialog.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "libslic3r/Preset.hpp"
#include "I18N.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <string>
#include <sstream>
#include <ios>
#include <iomanip>
#include <wx/anybutton.h>
#include <wx/checklst.h>
#include "slic3r/GUI/GUI_Utils.hpp"
#include "slic3r/GUI/Widgets/Button.hpp"
#include "slic3r/GUI/Widgets/Label.hpp"
#include <utility>
#include "slic3r/GUI/wxExtensions.hpp"
#include "slic3r/GUI/Widgets/HyperLink.hpp"
#include <cstdlib>
#include <cstdio>
#include "slic3r/GUI/DeviceCore/DevDefs.h"
#include "libslic3r/calib.hpp"
#include "libslic3r/Config.hpp"
#include <vector>
#include <unordered_map>
#include <wx/arrstr.h>
#include <set>
#include "slic3r/GUI/DeviceCore/DevConfigUtil.h"
#include <cstddef>
#include <climits>
#include "libslic3r/PrintConfig.hpp"
#include <cmath>
#include "slic3r/GUI/Widgets/PopupWindow.hpp"
#include "slic3r/GUI/Widgets/StaticBox.hpp"
#include <wx/colordlg.h>
#include <wx/dcclient.h>
#include <wx/dc.h>
#include <wx/colour.h>
#include <wx/colourdata.h>
#include <wx/dcgraph.h>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <wx/toplevel.h>
#include <wx/panel.h>
#include <wx/textctrl.h>
#include <wx/string.h>
#include <wx/valtext.h>
#include <wx/sizer.h>
#include <wx/peninfobase.h>
#include "CalibUtils.hpp"
#include "../Utils/ColorSpaceConvert.hpp"
#include "EncodedFilament.hpp"
#include "DeviceCore/DevConfig.h"
#include "DeviceCore/DevExtruderSystem.h"
#include "DeviceCore/DevFilaBlackList.h"
#include "DeviceCore/DevFilaSystem.h"
#include "DeviceCore/DevFilaSwitch.h"
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle / GetNozzleByPosId / GetNozzleRack
#include "DeviceCore/DevNozzleRack.h" // DevNozzleRack::IsSupported (rack gating)
#define FILAMENT_MAX_TEMP 300
#define FILAMENT_MIN_TEMP 120
namespace Slic3r { namespace GUI {
wxDEFINE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent);
static std::string float_to_string_with_precision(float value, int precision = 3)
{
if (value < 0)
return std::string();
std::stringstream stream;
stream << std::fixed << std::setprecision(precision) << value;
return stream.str();
}
AMSMaterialsSetting::AMSMaterialsSetting(wxWindow *parent, wxWindowID id)
: DPIDialog(parent, id, _L("AMS Materials Setting"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
, m_color_picker_popup(ColorPickerPopup(this))
{
create();
wxGetApp().UpdateDlgDarkUI(this);
}
void AMSMaterialsSetting::create()
{
SetBackgroundColour(*wxWHITE);
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
m_panel_normal = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
create_panel_normal(m_panel_normal);
m_panel_kn = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
create_panel_kn(m_panel_kn);
wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL);
m_sizer_button->Add(0, 0, 1, wxEXPAND, 0);
m_button_confirm = new Button(this, _L("Confirm"));
m_button_confirm->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
m_button_confirm->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_ok, this);
m_button_reset = new Button(this, _L("Reset"));
m_button_reset->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
m_button_reset->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_reset, this);
m_button_close = new Button(this, _L("Close"));
m_button_close->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
m_button_close->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_close, this);
m_sizer_button->Add(m_button_confirm, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(20));
m_sizer_button->Add(m_button_reset, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(20));
m_sizer_button->Add(m_button_close, 0, wxALIGN_CENTER, 0);
m_sizer_main->Add(m_panel_normal, 0, wxALL, FromDIP(2));
m_sizer_main->Add(m_panel_kn, 0, wxALL, FromDIP(2));
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(24));
m_sizer_main->Add(m_sizer_button, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20));
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(16));
SetSizer(m_sizer_main);
Layout();
Fit();
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& e) {
warning_text->Hide();
Layout();
Fit();
e.Skip();
});
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent& e) {
input_min_finish();
e.Skip();
});
m_input_nozzle_min->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
input_min_finish();
e.Skip();
});
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& e) {
warning_text->Hide();
Layout();
Fit();
e.Skip();
});
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent& e) {
input_max_finish();
e.Skip();
});
m_input_nozzle_max->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
input_max_finish();
e.Skip();
});
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_cali_result->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_cali_result), NULL, this);
}
void AMSMaterialsSetting::create_panel_normal(wxWindow* parent)
{
auto sizer = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* m_sizer_filament = new wxBoxSizer(wxHORIZONTAL);
m_title_filament = new wxStaticText(parent, wxID_ANY, _L("Filament"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
m_title_filament->SetFont(::Label::Body_13);
m_title_filament->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_filament->Wrap(-1);
m_sizer_filament->Add(m_title_filament, 0, wxALIGN_CENTER, 0);
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);
// make the style the same with disable m_input_k_val, FIXME
m_readonly_filament = new TextInput(parent, wxEmptyString, "", "", wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, wxTE_CENTRE | wxTE_PROCESS_ENTER);
m_readonly_filament->SetBorderColor(StateColor(std::make_pair(0xDBDBDB, (int)StateColor::Focused), std::make_pair(0x009688, (int)StateColor::Hovered),
std::make_pair(0xDBDBDB, (int)StateColor::Normal)));
m_readonly_filament->SetFont(::Label::Body_14);
m_readonly_filament->SetLabelColor(AMS_MATERIALS_SETTING_GREY800);
m_readonly_filament->GetTextCtrl()->Bind(wxEVT_SET_FOCUS, [](auto& e) {});
m_readonly_filament->GetTextCtrl()->Hide();
m_readonly_filament->Disable();
m_sizer_filament->Add(m_readonly_filament, 1, wxALIGN_CENTER, 0);
m_readonly_filament->Hide();
wxBoxSizer* m_sizer_colour = new wxBoxSizer(wxHORIZONTAL);
m_title_colour = new wxStaticText(parent, wxID_ANY, _L("Color"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
m_title_colour->SetFont(::Label::Body_13);
m_title_colour->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_colour->Wrap(-1);
m_sizer_colour->Add(m_title_colour, 0, wxALIGN_CENTER, 0);
m_sizer_colour->Add(0, 0, 0, wxEXPAND, 0);
m_clr_picker = new ColorPicker(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_clr_picker->set_show_full(true);
m_clr_picker->SetBackgroundColour(*wxWHITE);
m_clr_picker->Bind(wxEVT_LEFT_DOWN, &AMSMaterialsSetting::on_clr_picker, this);
m_sizer_colour->Add(m_clr_picker, 0, 0, 0);
m_clr_name = new Label(parent, wxEmptyString);
m_clr_name->SetForegroundColour(*wxBLACK);
m_clr_name->SetBackgroundColour(*wxWHITE);
m_clr_name->SetFont(Label::Body_13);
m_sizer_colour->Add(m_clr_name, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(10));
wxBoxSizer* m_sizer_temperature = new wxBoxSizer(wxHORIZONTAL);
m_title_temperature = new wxStaticText(parent, wxID_ANY, _L("Nozzle\nTemperature"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
m_title_temperature->SetFont(::Label::Body_13);
m_title_temperature->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_temperature->Wrap(-1);
m_sizer_temperature->Add(m_title_temperature, 0, wxALIGN_CENTER, 0);
m_sizer_temperature->Add(0, 0, 0, wxEXPAND, 0);
wxBoxSizer* sizer_other = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* sizer_tempinput = new wxBoxSizer(wxHORIZONTAL);
m_input_nozzle_max = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER);
m_input_nozzle_min = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE, wxTE_CENTRE | wxTE_PROCESS_ENTER);
m_input_nozzle_max->Enable(false);
m_input_nozzle_min->Enable(false);
m_input_nozzle_max->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
m_input_nozzle_min->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
degree = new ScalableBitmap(parent, "degree", 16);
bitmap_max_degree = new wxStaticBitmap(parent, -1, degree->bmp(), wxDefaultPosition, wxDefaultSize);
bitmap_min_degree = new wxStaticBitmap(parent, -1, degree->bmp(), wxDefaultPosition, wxDefaultSize);
sizer_tempinput->Add(m_input_nozzle_max, 1, wxALIGN_CENTER, 0);
sizer_tempinput->Add(bitmap_min_degree, 0, wxALIGN_CENTER, 0);
sizer_tempinput->Add(FromDIP(10), 0, 0, 0);
sizer_tempinput->Add(m_input_nozzle_min, 1, wxALIGN_CENTER, 0);
sizer_tempinput->Add(bitmap_max_degree, 0, wxALIGN_CENTER, 0);
wxBoxSizer* sizer_temp_txt = new wxBoxSizer(wxHORIZONTAL);
auto m_title_max = new wxStaticText(parent, wxID_ANY, _L("max"), wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE);
m_title_max->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_max->SetFont(::Label::Body_13);
auto m_title_min = new wxStaticText(parent, wxID_ANY, _L("min"), wxDefaultPosition, AMS_MATERIALS_SETTING_INPUT_SIZE);
m_title_min->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_min->SetFont(::Label::Body_13);
sizer_temp_txt->Add(m_title_max, 1, wxALIGN_CENTER, 0);
sizer_temp_txt->Add(FromDIP(10), 0, 0, 0);
sizer_temp_txt->Add(m_title_min, 1, wxALIGN_CENTER | wxRIGHT, FromDIP(16));
sizer_other->Add(sizer_temp_txt, 0, wxALIGN_CENTER, 0);
sizer_other->Add(sizer_tempinput, 0, wxALIGN_CENTER, 0);
m_sizer_temperature->Add(sizer_other, 0, wxALL | wxALIGN_CENTER, 0);
m_sizer_temperature->AddStretchSpacer();
wxString warning_string = wxString::FromUTF8(
(boost::format(_u8L("The input value should be greater than %1% and less than %2%")) % FILAMENT_MIN_TEMP % FILAMENT_MAX_TEMP).str());
warning_text = new wxStaticText(parent, wxID_ANY, warning_string, wxDefaultPosition, wxDefaultSize, 0);
warning_text->SetFont(::Label::Body_13);
warning_text->SetForegroundColour(wxColour(255, 111, 0));
warning_text->Wrap(AMS_MATERIALS_SETTING_BODY_WIDTH);
warning_text->SetMinSize(wxSize(AMS_MATERIALS_SETTING_BODY_WIDTH, -1));
warning_text->Hide();
m_panel_SN = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
wxBoxSizer* m_sizer_SN = new wxBoxSizer(wxVERTICAL);
m_sizer_SN->AddSpacer(FromDIP(16));
wxBoxSizer* m_sizer_SN_inside = new wxBoxSizer(wxHORIZONTAL);
auto m_title_SN = new wxStaticText(m_panel_SN, wxID_ANY, _L("SN"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
m_title_SN->SetFont(::Label::Body_13);
m_title_SN->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_SN->Wrap(-1);
m_sizer_SN_inside->Add(m_title_SN, 0, wxALIGN_CENTER, 0);
m_sizer_SN_inside->Add(0, 0, 0, wxEXPAND, 0);
m_sn_number = new wxStaticText(m_panel_SN, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize);
m_sn_number->SetForegroundColour(*wxBLACK);
m_sizer_SN_inside->Add(m_sn_number, 0, wxALIGN_CENTER, 0);
m_sizer_SN->Add(m_sizer_SN_inside);
m_panel_SN->SetSizer(m_sizer_SN);
m_panel_SN->Layout();
m_panel_SN->Fit();
wxBoxSizer* m_tip_sizer = new wxBoxSizer(wxHORIZONTAL);
m_tip_readonly = new Label(parent, "");
m_tip_readonly->SetForegroundColour(*wxBLACK);
m_tip_readonly->SetBackgroundColour(*wxWHITE);
m_tip_readonly->SetMinSize(wxSize(FromDIP(380), -1));
m_tip_readonly->SetMaxSize(wxSize(FromDIP(380), -1));
m_tip_readonly->Hide();
m_tip_sizer->Add(m_tip_readonly, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(16));
sizer->Add(m_sizer_filament, 0, wxLEFT | wxRIGHT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(16));
sizer->Add(m_sizer_colour, 0, wxLEFT | wxRIGHT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(16));
sizer->Add(m_sizer_temperature, 0, wxLEFT | wxRIGHT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(5));
sizer->Add(warning_text, 0, wxLEFT | wxRIGHT, FromDIP(20));
sizer->Add(m_panel_SN, 0, wxLEFT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(24));
sizer->Add(m_tip_sizer, 0, wxLEFT, FromDIP(20));
parent->SetSizer(sizer);
}
void AMSMaterialsSetting::create_panel_kn(wxWindow* parent)
{
auto sizer = new wxBoxSizer(wxVERTICAL);
auto cali_title_sizer = new wxBoxSizer(wxHORIZONTAL);
// title
m_ratio_text = new wxStaticText(parent, wxID_ANY, _L("Factors of Flow Dynamics Calibration"));
m_ratio_text->SetForegroundColour(wxColour(50, 58, 61));
m_ratio_text->SetFont(Label::Head_14);
// Orca: link to the Orca Slicer pressure-advance wiki (region-agnostic).
wxString link_url = "https://www.orcaslicer.com/wiki/pressure_advance_calib";
m_wiki_ctrl = new HyperLink(parent, _L("Wiki Guide"), link_url);
cali_title_sizer->Add(m_ratio_text, 0, wxALIGN_CENTER_VERTICAL);
cali_title_sizer->Add(m_wiki_ctrl, 0, wxALIGN_CENTER_VERTICAL);
wxBoxSizer *m_sizer_cali_resutl = new wxBoxSizer(wxHORIZONTAL);
// pa profile
m_title_pa_profile = new wxStaticText(parent, wxID_ANY, _L("PA Profile"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0);
m_title_pa_profile->SetMinSize(wxSize(FromDIP(80), -1));
m_title_pa_profile->SetMaxSize(wxSize(FromDIP(80), -1));
m_title_pa_profile->SetFont(::Label::Body_13);
m_title_pa_profile->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800);
m_title_pa_profile->Wrap(-1);
m_sizer_cali_resutl->Add(m_title_pa_profile, 0, wxALIGN_CENTER, 0);
m_sizer_cali_resutl->Add(0, 0, 0, wxEXPAND, 0);
m_comboBox_cali_result = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY);
m_sizer_cali_resutl->Add(m_comboBox_cali_result, 1, wxALIGN_CENTER, 0);
auto kn_val_sizer = new wxFlexGridSizer(0, 2, 0, 0);
kn_val_sizer->SetFlexibleDirection(wxBOTH);
kn_val_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
kn_val_sizer->AddGrowableCol(1);
// k params input
m_k_param = new wxStaticText(parent, wxID_ANY, _L("Factor K"), wxDefaultPosition, wxDefaultSize, 0);
m_k_param->SetMinSize(wxSize(FromDIP(80), -1));
m_k_param->SetMaxSize(wxSize(FromDIP(80), -1));
m_k_param->SetFont(::Label::Body_13);
m_k_param->SetForegroundColour(wxColour(50, 58, 61));
m_k_param->Wrap(-1);
kn_val_sizer->Add(m_k_param, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));
m_input_k_val = new TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE | wxTE_PROCESS_ENTER);
m_input_k_val->SetMinSize(wxSize(FromDIP(245), -1));
m_input_k_val->SetMaxSize(wxSize(FromDIP(245), -1));
m_input_k_val->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
kn_val_sizer->Add(m_input_k_val, 0, wxALL | wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(0));
// n params input
wxBoxSizer* n_sizer = new wxBoxSizer(wxHORIZONTAL);
m_n_param = new wxStaticText(parent, wxID_ANY, _L("Factor N"), wxDefaultPosition, wxDefaultSize, 0);
m_n_param->SetFont(::Label::Body_13);
m_n_param->SetForegroundColour(wxColour(50, 58, 61));
m_n_param->Wrap(-1);
kn_val_sizer->Add(m_n_param, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5));
m_input_n_val = new TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE | wxTE_PROCESS_ENTER);
m_input_n_val->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
kn_val_sizer->Add(m_input_n_val, 0, wxALL | wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(5));
m_n_param->Hide();
m_input_n_val->Hide();
sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
sizer->Add(cali_title_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(12));
sizer->Add(m_sizer_cali_resutl, 0, wxLEFT | wxRIGHT, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
sizer->Add(kn_val_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20));
sizer->Add(0, 0, 0, wxTOP, FromDIP(10));
parent->SetSizer(sizer);
}
void AMSMaterialsSetting::paintEvent(wxPaintEvent &evt)
{
auto size = GetSize();
wxPaintDC dc(this);
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#000000")), 1, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
dc.DrawRectangle(0, 0, size.x, size.y);
}
AMSMaterialsSetting::~AMSMaterialsSetting()
{
m_comboBox_filament->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this);
m_comboBox_cali_result->Disconnect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_cali_result), NULL, this);
}
void AMSMaterialsSetting::input_min_finish()
{
if (m_input_nozzle_min->GetTextCtrl()->GetValue().empty()) return;
auto val = std::atoi(m_input_nozzle_min->GetTextCtrl()->GetValue().c_str());
if (val < FILAMENT_MIN_TEMP || val > FILAMENT_MAX_TEMP) {
warning_text->Show();
} else {
warning_text->Hide();
}
Layout();
Fit();
}
void AMSMaterialsSetting::input_max_finish()
{
if (m_input_nozzle_max->GetTextCtrl()->GetValue().empty()) return;
auto val = std::atoi(m_input_nozzle_max->GetTextCtrl()->GetValue().c_str());
if (val < FILAMENT_MIN_TEMP || val > FILAMENT_MAX_TEMP) {
warning_text->Show();
}
else {
warning_text->Hide();
}
Layout();
Fit();
}
void AMSMaterialsSetting::update()
{
if (obj) {
update_widgets();
update_filament_editing(obj->is_in_printing() || obj->can_resume());
}
}
void AMSMaterialsSetting::update_filament_editing(bool is_printing)
{
if (is_printing) {
m_comboBox_filament->Enable(obj->is_support_filament_setting_inprinting);
m_comboBox_cali_result->Enable(obj->is_support_filament_setting_inprinting);
m_button_confirm->Show(obj->is_support_filament_setting_inprinting);
m_button_reset->Show(obj->is_support_filament_setting_inprinting);
}
else {
m_comboBox_filament->Enable(true);
m_comboBox_cali_result->Enable(true);
m_button_reset->Show(true);
m_button_confirm->Show(true);
}
if (!m_is_third) {
m_tip_readonly->SetLabelText(wxEmptyString);
m_tip_readonly->Hide();
}
else {
if (!obj->is_support_filament_setting_inprinting) {
if (!is_virtual_tray()) {
m_tip_readonly->SetLabelText(_L("Setting AMS slot information while printing is not supported"));
} else {
m_tip_readonly->SetLabelText(_L("Setting Virtual slot information while printing is not supported"));
}
} else {
m_tip_readonly->SetLabelText(wxEmptyString);
}
m_tip_readonly->Wrap(FromDIP(380));
m_tip_readonly->Show(is_printing);
}
if (m_view_only) { // Orca: view-only (2D laser/cut) — lock every edit control and hide apply/reset
m_comboBox_filament->Enable(false);
m_comboBox_cali_result->Enable(false);
m_input_k_val->Enable(false);
m_input_n_val->Enable(false);
m_button_confirm->Hide();
m_button_reset->Hide();
}
}
void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
if (m_view_only) return; // Orca: view-only never commits
MessageDialog msg_dlg(nullptr, _L("Are you sure you want to clear the filament information?"), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL);
auto result = msg_dlg.ShowModal();
if (result != wxID_OK)
return;
m_input_nozzle_min->GetTextCtrl()->SetValue("");
m_input_nozzle_max->GetTextCtrl()->SetValue("");
ams_filament_id = "";
ams_setting_id = "";
m_filament_selection = -1;
wxString k_text = "0.000";
wxString n_text = "0.000";
m_filament_type = "";
long nozzle_temp_min_int = 0;
long nozzle_temp_max_int = 0;
wxColour color = *wxWHITE;
char col_buf[10];
sprintf(col_buf, "%02X%02X%02X00", (int)color.Red(), (int)color.Green(), (int)color.Blue());
std::string color_str; // reset use empty string
std::string selected_ams_id;
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
if (preset_bundle) {
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) {
selected_ams_id = it->filament_id;
break;
}
}
}
if (obj) {
if(m_is_third){
obj->command_ams_filament_settings(ams_id, slot_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int,
nozzle_temp_max_int);
}
// set k / n value
if (obj->cali_version <= -1 && obj->get_printer_series() == PrinterSeries::SERIES_P1P) {
// set extrusion cali ratio
int cali_tray_id = ams_id * 4 + slot_id;
double k = 0.0;
try {
k_text.ToDouble(&k);
}
catch (...) {
;
}
double n = 0.0;
try {
n_text.ToDouble(&n);
}
catch (...) {
;
}
obj->command_extrusion_cali_set(cali_tray_id, "", "", k, n);
}
else {
PACalibIndexInfo select_index_info;
int tray_id = ams_id * 4 + slot_id;
if (is_virtual_tray()) {
tray_id = ams_id;
if (!obj->is_enable_np) {
tray_id = VIRTUAL_TRAY_DEPUTY_ID;
}
}
select_index_info.tray_id = tray_id;
select_index_info.ams_id = ams_id;
select_index_info.slot_id = slot_id;
select_index_info.nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
select_index_info.cali_idx = -1;
select_index_info.filament_id = selected_ams_id;
CalibUtils::select_PA_calib_result(select_index_info);
}
}
Close();
}
// Nozzle-context builder for the AMS-edit blacklist check. Factors the blacklist evaluation out of
// on_select_ok and threads rack-gated per-nozzle context (extruder id + nozzle flow + nozzle diameter)
// into CheckFilamentInfo, returning the accumulate-all CheckResult. As a side effect it resolves
// ams_filament_id/ams_setting_id from the matched preset. fila_name is kept as the preset name (not
// the short alias) so the name / name_suffix rules match on the non-rack fleet (no unintended
// activation of e.g. the PLA Glow rule).
static DevFilaBlacklist::CheckResult
sCheckFilamentInfo(PresetBundle* preset_bundle,
MachineObject* obj,
int ams_id,
int slot_id,
const std::string& filament_id,
std::string& ams_filament_id,
std::string& ams_setting_id)
{
DevFilaBlacklist::CheckResult result;
if (!preset_bundle || !obj)
return result;
// Orca: there is no lookup struct that also carries setting_id, so resolve the (root) preset by
// scanning the filaments collection for a matching filament_id.
Preset* fila_preset = nullptr;
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); ++it) {
if (it->filament_id == filament_id) {
fila_preset = &(*it);
break;
}
}
if (!fila_preset)
return result;
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
std::string filamnt_type;
fila_preset->get_filament_type(filamnt_type);
DevFilaBlacklist::CheckFilamentInfo check_info;
check_info.dev_id = obj->get_dev_id();
check_info.model_id = obj->printer_type;
check_info.fila_id = fila_preset->filament_id;
check_info.fila_type = filamnt_type;
check_info.fila_name = fila_preset->name;
check_info.has_filament_switch = obj->GetFilaSwitch()->IsInstalled();
check_info.ams_id = ams_id;
check_info.slot_id = slot_id;
if (auto vendor = dynamic_cast<ConfigOptionStrings*>(fila_preset->config.option("filament_vendor"));
vendor && !vendor->values.empty()) {
check_info.fila_vendor = vendor->values[0];
}
check_info.extruder_id = obj->GetFilaSystem()->GetExtruderIdByAmsId(std::to_string(ams_id));
// Rack-gated per-nozzle context. For a multi-nozzle main extruder on a rack printer (H2C) the
// specific nozzle is resolved later by the print-dispatch rack mapping, so leave
// nozzle_flow/nozzle_diameter unset here to keep the high-flow nozzle rules dormant. Every
// non-rack printer takes the else branch, threading the reported nozzle flow + diameter and
// thereby activating the high-flow blacklist warnings in the AMS-edit dialog for H2D/H2S/X2D/P2S
// (and the E3D-kit X1C/P1x).
DevNozzleSystem* nozzle_system = obj->GetNozzleSystem();
const bool rack_supported = nozzle_system && nozzle_system->GetNozzleRack() &&
nozzle_system->GetNozzleRack()->IsSupported();
if (check_info.extruder_id == MAIN_EXTRUDER_ID && rack_supported) {
; // multi-nozzle main extruder on a rack printer — nozzle resolved later by print dispatch
} else {
check_info.nozzle_flow = obj->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id));
if (nozzle_system) {
DevNozzle nozzle = nozzle_system->GetNozzleByPosId(check_info.extruder_id.value_or(-1));
if (!nozzle.IsEmpty())
check_info.nozzle_diameter = nozzle.GetNozzleDiameter();
}
}
result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
}
ams_filament_id = fila_preset->filament_id;
ams_setting_id = fila_preset->setting_id;
return result;
}
void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
{
if (!obj)
return;
if (m_view_only) return; // Orca: view-only never commits
//get filament id
ams_filament_id = "";
ams_setting_id = "";
// the combobox item
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
// check filament info (rack-gated per-nozzle blacklist evaluation)
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const auto& fila_check_res = sCheckFilamentInfo(preset_bundle, obj, ams_id, slot_id,
filament_item.filament_id, ams_filament_id, ams_setting_id);
if (const auto& prohibit_items = fila_check_res.get_items_by_action("prohibition"); !prohibit_items.empty()) {
wxString info_msg;
for (const auto& item : prohibit_items) { info_msg += item.info_msg + "\n"; }
MessageDialog msg_wingow(nullptr, info_msg, _L("Error"), wxICON_WARNING | wxOK);
msg_wingow.ShowModal();
return;
}
if (const auto& warning_items = fila_check_res.get_items_by_action("warning"); !warning_items.empty()) {
std::vector<FilamentWarningInfo> infos;
for (const auto& item : warning_items) {
FilamentWarningInfo winfo;
winfo.info_msg = item.info_msg;
winfo.wiki_url = item.wiki_url;
infos.emplace_back(winfo);
}
FilamentWarningDialog msg_window(nullptr, _L("Warning"), infos);
msg_window.ShowModal();
}
wxString nozzle_temp_min = m_input_nozzle_min->GetTextCtrl()->GetValue();
auto filament = m_comboBox_filament->GetValue();
wxString nozzle_temp_max = m_input_nozzle_max->GetTextCtrl()->GetValue();
long nozzle_temp_min_int, nozzle_temp_max_int;
nozzle_temp_min.ToLong(&nozzle_temp_min_int);
nozzle_temp_max.ToLong(&nozzle_temp_max_int);
wxColour color = m_clr_picker->m_colour;
char col_buf[10];
sprintf(col_buf, "%02X%02X%02X%02X", (int)color.Red(), (int)color.Green(), (int)color.Blue(), (int)color.Alpha());
if (ams_filament_id.empty() || nozzle_temp_min.empty() || nozzle_temp_max.empty() || m_filament_type.empty()) {
BOOST_LOG_TRIVIAL(trace) << "Invalid Setting id";
MessageDialog msg_dlg(nullptr, _L("You need to select the material type and color first."), wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
// Orca: log the tray payload this dialog hands the printer, so the filament_id resolved from the
// dropdown selection can be checked against the tray_info_idx the AMS actually receives. A
// BBL-tagged (RFID) tray is read-only here, so nothing is published for it.
BOOST_LOG_TRIVIAL(info) << "ams_materials_setting: " << (m_is_third ? "sending" : "NOT sending (BBL RFID tray, read-only)")
<< ", ams_id = " << ams_id << ", slot_id = " << slot_id
<< ", selected = " << m_comboBox_filament->GetValue().ToStdString()
<< ", tray_info_idx (filament_id) = " << ams_filament_id
<< ", setting_id = " << ams_setting_id
<< ", tray_type = " << m_filament_type
<< ", tray_color = " << col_buf
<< ", nozzle_temp_min = " << nozzle_temp_min_int
<< ", nozzle_temp_max = " << nozzle_temp_max_int;
// set filament
if (m_is_third) {
obj->command_ams_filament_settings(ams_id, slot_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int);
}
//reset param
wxString k_text = m_input_k_val->GetTextCtrl()->GetValue();
wxString n_text = m_input_n_val->GetTextCtrl()->GetValue();
if (obj->cali_version <= -1 && (obj->get_printer_series() != PrinterSeries::SERIES_X1) && !ExtrusionCalibration::check_k_validation(k_text)) {
wxString k_tips = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE);
wxString kn_tips = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f, N in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE, 0.6, 2.0);
MessageDialog msg_dlg(nullptr, k_tips, wxEmptyString, wxICON_WARNING | wxOK);
msg_dlg.ShowModal();
return;
}
// set k / n value
if (is_virtual_tray()) {
double k = 0.0;
try {
k_text.ToDouble(&k);
}
catch (...) {
;
}
double n = 0.0;
try {
n_text.ToDouble(&n);
}
catch (...) {
;
}
auto vt_tray = ams_id;
if (!obj->is_enable_np) {
vt_tray = VIRTUAL_TRAY_DEPUTY_ID;
}
if (obj->cali_version >= 0) {
PACalibIndexInfo select_index_info;
select_index_info.tray_id = vt_tray;
select_index_info.ams_id = ams_id;
select_index_info.slot_id = 0;
select_index_info.nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
auto cali_select_id = m_comboBox_cali_result->GetSelection();
if (m_pa_profile_items.size() > 0 && cali_select_id >= 0) {
select_index_info.cali_idx = m_pa_profile_items[cali_select_id].cali_idx;
select_index_info.filament_id = m_pa_profile_items[cali_select_id].filament_id;
}
else { // default item
select_index_info.cali_idx = -1;
select_index_info.filament_id = ams_filament_id;
}
CalibUtils::select_PA_calib_result(select_index_info);
}
else {
obj->command_extrusion_cali_set(vt_tray, "", "", k, n);
}
}
else {
int cali_tray_id = ams_id * 4 + slot_id;
double k = 0.0;
try {
k_text.ToDouble(&k);
}
catch (...) {
;
}
double n = 0.0;
try {
n_text.ToDouble(&n);
}
catch (...) {
;
}
if (obj->cali_version >= 0) {
PACalibIndexInfo select_index_info;
select_index_info.tray_id = cali_tray_id;
select_index_info.ams_id = ams_id;
select_index_info.slot_id = slot_id;
select_index_info.nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
auto cali_select_id = m_comboBox_cali_result->GetSelection();
if (m_pa_profile_items.size() > 0 && cali_select_id > 0) {
select_index_info.cali_idx = m_pa_profile_items[cali_select_id].cali_idx;
select_index_info.filament_id = m_pa_profile_items[cali_select_id].filament_id;
}
else { // default item
select_index_info.cali_idx = -1;
select_index_info.filament_id = ams_filament_id;
}
CalibUtils::select_PA_calib_result(select_index_info);
}
else {
obj->command_extrusion_cali_set(cali_tray_id, "", "", k, n);
}
}
Close();
}
void AMSMaterialsSetting::on_select_close(wxCommandEvent &event)
{
Close();
}
void AMSMaterialsSetting::set_color(wxColour color)
{
//m_clrData->SetColour(color);
m_clr_picker->is_empty(false);
m_clr_picker->set_color(color);
FilamentColor fila_color;
fila_color.m_colors.insert(color);
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
// ams_filament_id is our OF id; GetFilaColorName looks up filaments_color_codes.json,
// downloaded from Bambu and keyed by the printer's own ids, so translate for this lookup only.
auto* agent = GUI::wxGetApp().getAgent();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(agent ? agent->from_orca_filament_id(ams_filament_id) : ams_filament_id, fila_color));
}
void AMSMaterialsSetting::set_empty_color(wxColour color)
{
m_clr_picker->is_empty(true);
m_clr_picker->set_color(color);
m_clr_name->SetLabelText(wxEmptyString);
}
void AMSMaterialsSetting::set_colors(std::vector<wxColour> colors)
{
//m_clrData->SetColour(color);
m_clr_picker->set_colors(colors);
if (!colors.empty())
{
FilamentColor fila_color;
for (const auto& clr : colors) { fila_color.m_colors.insert(clr); }
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
// ams_filament_id is our OF id; GetFilaColorName looks up filaments_color_codes.json,
// downloaded from Bambu and keyed by the printer's own ids, so translate for this lookup only.
auto* agent = GUI::wxGetApp().getAgent();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(agent ? agent->from_orca_filament_id(ams_filament_id) : ams_filament_id, fila_color));
}
}
void AMSMaterialsSetting::set_ctype(int ctype)
{
m_clr_picker->ctype = ctype;
}
void AMSMaterialsSetting::on_picker_color(wxCommandEvent& event)
{
unsigned int color_num = event.GetInt();
set_color(wxColour(color_num>>24&0xFF, color_num>>16&0xFF, color_num>>8&0xFF, color_num&0xFF));
}
void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event)
{
if (m_view_only) return; // Orca: view-only disables color editing
if(!m_is_third)
return;
if (obj->is_in_printing() || obj->can_resume()) {
if (!obj->is_support_filament_setting_inprinting) {
return;
}
}
std::vector<wxColour> ams_colors;
obj->GetFilaSystem()->CollectAmsColors(ams_colors);
wxPoint img_pos = m_clr_picker->ClientToScreen(wxPoint(0, 0));
wxPoint popup_pos(img_pos.x - m_color_picker_popup.GetSize().x - FromDIP(95), img_pos.y - FromDIP(65));
m_color_picker_popup.Position(popup_pos, wxSize(0, 0));
m_color_picker_popup.set_ams_colours(ams_colors);
m_color_picker_popup.set_def_colour(m_clr_picker->m_colour);
m_color_picker_popup.Popup();
}
bool AMSMaterialsSetting::is_virtual_tray()
{
if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID)
return true;
return false;
}
void AMSMaterialsSetting::update_widgets()
{
if (obj && obj->get_printer_series() == PrinterSeries::SERIES_X1 && obj->cali_version <= -1) {
// Low version firmware does not display k value
m_panel_kn->Hide();
}
else if(is_virtual_tray()) // virtual tray
{
if (obj)
m_panel_normal->Show();
else
m_panel_normal->Hide();
m_panel_kn->Show();
} else if (obj && (obj->ams_support_virtual_tray || obj->cali_version >= 0)) {
m_panel_normal->Show();
m_panel_kn->Show();
} else {
m_panel_normal->Show();
m_panel_kn->Hide();
}
Layout();
}
bool AMSMaterialsSetting::Show(bool show)
{
if (show) {
m_button_confirm->Rescale(); // ORCA re applies size
m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
//m_clr_picker->set_color(m_clr_picker->GetParent()->GetBackgroundColour());
m_ratio_text->Show();
m_wiki_ctrl->Show();
m_k_param->Show();
m_input_k_val->Show();
Layout();
Fit();
wxGetApp().UpdateDlgDarkUI(this);
}
return DPIDialog::Show(show);
}
static void _collect_filament_info(const wxString& shown_name,
const Preset& filament,
std::unordered_map<wxString, wxString>& query_filament_vendors,
std::unordered_map<wxString, wxString>& query_filament_types)
{
query_filament_vendors[shown_name] = filament.config.get_filament_vendor();
query_filament_types[shown_name] = filament.config.get_filament_type();
}
void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_min, wxString temp_max, wxString k, wxString n)
{
if (!obj) return;
update_widgets();
// set default value
if (k.IsEmpty())
k = "0.000";
if (n.IsEmpty())
n = "0.000";
m_input_k_val->GetTextCtrl()->SetValue(k);
m_input_n_val->GetTextCtrl()->SetValue(n);
wxArrayString filament_items;
wxString bambu_filament_name;
wxString hint_filament_name; // the hint type to be selected
std::unordered_map<wxString, wxString> query_filament_vendors;// some information for sort
std::unordered_map<wxString, wxString> query_filament_types; //
std::set<std::string> filament_id_set;
// 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();
PresetBundle * preset_bundle = wxGetApp().preset_bundle;
std::ostringstream stream;
// Defensive: this dialog is opened only from StatusPanel (BBL-only) today, so the fallback fires
// only during the brief BBL startup window before firmware reports nozzle info. Without this,
// the "0.0" lookup string returns an empty set and the filament dropdown goes blank.
float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
if (machine_diameter == 0.0f && preset_bundle) {
const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter");
if (opt) machine_diameter = static_cast<const ConfigOptionFloats *>(opt)->values[0];
}
stream << std::fixed << std::setprecision(1) << machine_diameter;
std::string nozzle_diameter_str = stream.str();
if (preset_bundle) {
BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << preset_bundle->filaments.size();
for (Preset *filament_it : preset_bundle->get_filament_presets_for_machine(
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
if (!filament_id_set.insert(filament_it->filament_id).second)
continue;
const std::string alias = preset_bundle->filaments.get_preset_alias(*filament_it, true);
if (alias.empty())
continue;
filament_items.push_back(alias);
_collect_filament_info(alias, *filament_it, query_filament_vendors, query_filament_types);
FilamentInfos filament_infos;
filament_infos.filament_id = filament_it->filament_id;
filament_infos.setting_id = filament_it->setting_id;
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);
// update if nozzle_temperature_range is found
const int variant_index = get_filament_variant_index(*filament_it, nozzle_diameter_str);
ConfigOption *opt_min = filament_it->config.option("nozzle_temperature_range_low");
if (opt_min) {
ConfigOptionInts *opt_min_ints = dynamic_cast<ConfigOptionInts *>(opt_min);
if (opt_min_ints) {
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index));
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
}
}
ConfigOption *opt_max = filament_it->config.option("nozzle_temperature_range_high");
if (opt_max) {
ConfigOptionInts *opt_max_ints = dynamic_cast<ConfigOptionInts *>(opt_max);
if (opt_max_ints) {
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index));
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
}
}
}
}
}
if (!sn.empty()) {
m_sn_number->SetLabel(sn);
m_panel_SN->Show();
}
else {
m_panel_SN->Hide();
}
if (obj) {
if (!m_is_third) {
m_comboBox_filament->Hide();
m_readonly_filament->Show();
if (bambu_filament_name.empty()) {
m_readonly_filament->SetLabel("Bambu " + filament);
}
else {
m_readonly_filament->SetLabel(bambu_filament_name);
}
m_input_nozzle_min->GetTextCtrl()->SetValue(temp_min);
m_input_nozzle_max->GetTextCtrl()->SetValue(temp_max);
}
else {
m_comboBox_filament->Show();
m_readonly_filament->Hide();
}
if (obj->cali_version >= 0) {
m_title_pa_profile->Show();
m_comboBox_cali_result->Show();
m_input_k_val->Disable();
}
else {
m_title_pa_profile->Hide();
m_comboBox_cali_result->Hide();
m_input_k_val->Enable();
}
m_button_reset->Show();
//m_button_confirm->Show();
}
// Sort the filaments
{
std::unordered_map<wxString, int> selected_filament_ranks;
// Helper lambda to find a filament Preset by name. We can call this multiple times to walk the inheritance chain and find the base filament.
auto find_filament_by_name = [](const std::string& wanted, const PresetCollection& filaments) -> const Preset* {
for (auto it = filaments.begin(); it != filaments.end(); ++it) {
if (it->name == wanted) {
return &(*it);
}
}
return nullptr;
};
// For each active filament preset, find its base filament alias and promote it in extruder order.
auto bundle = wxGetApp().preset_bundle;
const auto& preset_names = bundle->filament_presets;
for (size_t i = preset_names.size(); i-- > 0; ) {
std::string wanted = preset_names[i];
const int sort_rank = -static_cast<int>(preset_names.size() - i);
const Preset* match = nullptr;
do {
auto find_result = find_filament_by_name(wanted, bundle->filaments);
if (!find_result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " No available filament name matches " << wanted;
break;
}
match = find_result;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Found available filament matching current preset name " << wanted
<< " - Name: " << match->name << " - Alias: " << match->alias
<< " - Inherits: " << match->inherits();
if (match->inherits().length() == 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " No more inherits so we reached the base filament";
break;
}
wanted = match->inherits();
} while (1); // Or loop while (match->alias.length() == 0) because existence of alias and inherits on a Preset seem to be exclusive
if (!match) {
continue;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Update filament rank to " + std::to_string(sort_rank) + " for preset Name: "
<< match->name << " - Alias: " << match->alias;
selected_filament_ranks.insert_or_assign(match->alias, sort_rank);
}
static const std::vector<wxString> sorted_vendors { "Generic" };
static const std::vector<wxString> sorted_types { "PLA", "PETG", "ABS", "TPU" };
auto priority_rank = [](const std::vector<wxString>& priorities, const wxString& value) {
const auto iter = std::find_if(priorities.begin(), priorities.end(), [&value](const wxString& priority) {
return priority.CmpNoCase(value) == 0;
});
return iter - priorities.begin();
};
auto _filament_sorter = [&query_filament_vendors, &query_filament_types, &selected_filament_ranks, &priority_rank](const wxString& left, const wxString& right) -> bool
{
{ // Compare selected filament order
const auto& iter1 = selected_filament_ranks.find(left);
int selected_order1 = (iter1 != selected_filament_ranks.end()) ? iter1->second : INT_MAX;
const auto& iter2 = selected_filament_ranks.find(right);
int selected_order2 = (iter2 != selected_filament_ranks.end()) ? iter2->second : INT_MAX;
if (selected_order1 != selected_order2)
{
return selected_order1 < selected_order2;
}
}
{ // Compare vendor
const wxString& vendor1 = query_filament_vendors.at(left);
const wxString& vendor2 = query_filament_vendors.at(right);
const auto rank1 = priority_rank(sorted_vendors, vendor1);
const auto rank2 = priority_rank(sorted_vendors, vendor2);
if (rank1 != rank2)
return rank1 < rank2;
const int vendor_compare = vendor1.CmpNoCase(vendor2);
if (vendor_compare != 0)
return vendor_compare < 0;
}
{ // Compare type
const wxString& type1 = query_filament_types.at(left);
const wxString& type2 = query_filament_types.at(right);
const auto rank1 = priority_rank(sorted_types, type1);
const auto rank2 = priority_rank(sorted_types, type2);
if (rank1 != rank2)
return rank1 < rank2;
const int type_compare = type1.CmpNoCase(type2);
if (type_compare != 0)
return type_compare < 0;
}
const int name_compare = left.CmpNoCase(right);
return name_compare != 0 ? name_compare < 0 : left < right;
};
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;
}
}
}
m_comboBox_filament->Set(filament_items);
m_comboBox_filament->SetSelection(selection_idx);
post_select_event(selection_idx);
if (selection_idx < 0) {
m_comboBox_filament->SetValue(wxEmptyString);
}
// Set the flag whether to open the filament setting dialog from the device page
m_comboBox_filament->SetClientData(new int(1));
update();
Layout();
Fit();
ShowModal();
}
void AMSMaterialsSetting::post_select_event(int index) {
wxCommandEvent event(wxEVT_COMBOBOX);
event.SetInt(index);
event.SetEventObject(m_comboBox_filament);
wxPostEvent(m_comboBox_filament, event);
}
void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt)
{
m_pa_cali_select_id = evt.GetSelection();
if (m_pa_cali_select_id >= 0) {
m_input_k_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[m_pa_cali_select_id].k_value));
m_input_n_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[m_pa_cali_select_id].n_coef));
}
else{
m_input_k_val->GetTextCtrl()->SetValue(std::to_string(0.00));
m_input_n_val->GetTextCtrl()->SetValue(std::to_string(0.00));
}
}
int AMSMaterialsSetting::get_filament_variant_index(const Preset &filament, const std::string &nozzle_diameter_str)
{
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
if (!obj || !preset_bundle)
return 0;
const std::set<std::string> printer_names =
preset_bundle->get_printer_names_by_printer_type_and_nozzle(DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str);
const Preset *printer = printer_names.empty() ? nullptr : preset_bundle->printers.find_preset(*printer_names.begin());
if (!printer)
return 0;
const int extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
return PresetBundle::get_filament_variant_index(filament.config, printer->config, obj->get_preset_extruder_index(extruder_id),
DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id)));
}
void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
{
// Get the flag whether to open the filament setting dialog from the device page
int* from_printer = static_cast<int*>(m_comboBox_filament->GetClientData());
m_filament_type = "";
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
if (preset_bundle) {
std::ostringstream stream;
if (obj) {
// Defensive: this dialog is opened only from StatusPanel (BBL-only) today, so the fallback fires
// only during the brief BBL startup window before firmware reports nozzle info. Without this,
// the "0.0" lookup string returns an empty set and filament lookup yields no results.
float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
if (machine_diameter == 0.0f) {
const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter");
if (opt) machine_diameter = static_cast<const ConfigOptionFloats *>(opt)->values[0];
}
stream << std::fixed << std::setprecision(1) << machine_diameter;
}
std::string nozzle_diameter_str = stream.str();
// Resolve the selection against the same list Popup() built the dropdown from, so the two
// halves of the dialog cannot disagree about which filaments this machine can use.
const std::string selected = m_comboBox_filament->GetValue().ToStdString();
if (!selected.empty()) {
const std::string filament_id = map_filament_items[selected].filament_id;
for (Preset *it : preset_bundle->get_filament_presets_for_machine(
DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str, obj->is_support_user_preset)) {
if (it->filament_id != filament_id)
continue;
// ) if nozzle_temperature_range is found
const int variant_index = get_filament_variant_index(*it, nozzle_diameter_str);
ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low");
if (opt_min) {
ConfigOptionInts* opt_min_ints = dynamic_cast<ConfigOptionInts*>(opt_min);
if (opt_min_ints) {
wxString text_nozzle_temp_min = wxString::Format("%d", opt_min_ints->get_at(variant_index));
m_input_nozzle_min->GetTextCtrl()->SetValue(text_nozzle_temp_min);
}
}
ConfigOption* opt_max = it->config.option("nozzle_temperature_range_high");
if (opt_max) {
ConfigOptionInts* opt_max_ints = dynamic_cast<ConfigOptionInts*>(opt_max);
if (opt_max_ints) {
wxString text_nozzle_temp_max = wxString::Format("%d", opt_max_ints->get_at(variant_index));
m_input_nozzle_max->GetTextCtrl()->SetValue(text_nozzle_temp_max);
}
}
ConfigOption* opt_type = it->config.option("filament_type");
bool found_filament_type = false;
if (opt_type) {
ConfigOptionStrings* opt_type_strs = dynamic_cast<ConfigOptionStrings*>(opt_type);
if (opt_type_strs) {
found_filament_type = true;
//m_filament_type = opt_type_strs->get_at(0);
std::string display_filament_type;
m_filament_type = it->config.get_filament_type(display_filament_type);
}
}
if (!found_filament_type)
m_filament_type = "";
break;
}
}
}
if (m_input_nozzle_min->GetTextCtrl()->GetValue().IsEmpty()) {
m_input_nozzle_min->GetTextCtrl()->SetValue("0");
}
if (m_input_nozzle_max->GetTextCtrl()->GetValue().IsEmpty()) {
m_input_nozzle_max->GetTextCtrl()->SetValue("0");
}
m_filament_selection = evt.GetSelection();
//reset cali
int cali_select_idx = -1;
if ( !this->obj || m_filament_selection < 0) {
m_input_k_val->Enable(false);
m_input_n_val->Enable(false);
m_button_confirm->Disable(); // ORCA No need to change style
m_comboBox_cali_result->Clear();
m_comboBox_cali_result->SetValue(wxEmptyString);
m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString);
m_input_n_val->GetTextCtrl()->SetValue(wxEmptyString);
m_comboBox_filament->SetClientData(new int(0));
return;
}
else {
m_button_confirm->Enable(true); // ORCA No need to change style
}
//filament id
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;
}
}
}
wxArrayString items;
m_pa_profile_items.clear();
m_comboBox_cali_result->SetValue(wxEmptyString);
auto get_cali_index = [this](const std::string& str) -> int{
for (int i = 0; i < int(m_pa_profile_items.size()); ++i) {
if (m_pa_profile_items[i].name == str)
return i;
}
return 0;
};
int extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
if (obj->is_nozzle_flow_type_supported() && (obj->GetExtderSystem()->GetNozzleFlowType(extruder_id) == NozzleFlowType::NONE_FLOWTYPE))
{
MessageDialog dlg(nullptr, _L("The nozzle flow is not set. Please set the nozzle flow rate before editing the filament.\n'Device -> Print parts'"), _L("Warning"), wxICON_WARNING | wxOK);
dlg.ShowModal();
}
NozzleFlowType nozzle_flow_type = obj->GetExtderSystem()->GetNozzleFlowType(extruder_id);
NozzleVolumeType nozzle_volume_type = NozzleVolumeType::nvtStandard;
if (nozzle_flow_type != NozzleFlowType::NONE_FLOWTYPE)
{
nozzle_volume_type = NozzleVolumeType(nozzle_flow_type - 1);
}
if (obj->cali_version >= 0) {
// add default item
PACalibResult default_item;
default_item.cali_idx = -1;
default_item.filament_id = ams_filament_id;
if (obj->GetConfig()->SupportCalibrationPA_FlowAuto()) {
default_item.k_value = -1;
default_item.n_coef = -1;
}
else {
get_default_k_n_value(ams_filament_id, default_item.k_value, default_item.n_coef);
}
m_pa_profile_items.emplace_back(default_item);
items.push_back(_L("Default"));
m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString);
std::vector<PACalibResult> cali_history = this->obj->pa_calib_tab;
for (auto cali_item : cali_history) {
if (cali_item.filament_id == ams_filament_id) {
if (obj->is_multi_extruders() && (cali_item.extruder_id != extruder_id || cali_item.nozzle_volume_type != nozzle_volume_type)) {
continue;
}
items.push_back(from_u8(cali_item.name));
m_pa_profile_items.push_back(cali_item);
}
}
m_comboBox_cali_result->Set(items);
if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
if (from_printer && (*from_printer == 1)) {
for (auto slot : obj->vt_slot) {
if (slot.id == std::to_string(ams_id))
cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, slot.cali_idx);
}
if (cali_select_idx >= 0)
m_comboBox_cali_result->SetSelection(cali_select_idx);
else
m_comboBox_cali_result->SetSelection(0);
}
else {
int index = get_cali_index(m_comboBox_filament->GetLabel().ToStdString());
m_comboBox_cali_result->SetSelection(index);
}
}
else {
if (from_printer && (*from_printer == 1)) {
DevAmsTray* selected_tray = this->obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
if (!selected_tray)
{
return;
}
cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, selected_tray->cali_idx);
if (cali_select_idx < 0)
{
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_status_error: cannot find pa profile, ams_id = " << ams_id
<< ", slot_id = " << slot_id << ", cali_idx = " << selected_tray->cali_idx;
cali_select_idx = 0;
}
m_comboBox_cali_result->SetSelection(cali_select_idx);
}
else {
int index = get_cali_index(m_comboBox_filament->GetLabel().ToStdString());
m_comboBox_cali_result->SetSelection(index);
}
}
if (cali_select_idx >= 0) {
m_input_k_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[cali_select_idx].k_value));
m_input_n_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[cali_select_idx].n_coef));
}
else {
m_input_k_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[0].k_value));
m_input_n_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[0].n_coef));
}
}
else {
if (!ams_filament_id.empty()) {
//m_input_k_val->GetTextCtrl()->SetValue("0.00");
m_input_k_val->Enable(true);
}
else {
//m_input_k_val->GetTextCtrl()->SetValue("0.00");
m_input_k_val->Disable();
}
}
m_comboBox_filament->SetClientData(new int(0));
}
void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect)
{
m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
m_input_k_val->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20)));
m_clr_picker->msw_rescale();
degree->msw_rescale();
bitmap_max_degree->SetBitmap(degree->bmp());
bitmap_min_degree->SetBitmap(degree->bmp());
m_button_reset->Rescale(); // ORCA
m_button_confirm->Rescale(); // ORCA
m_button_close->Rescale(); // ORCA
this->Refresh();
}
ColorPicker::ColorPicker(wxWindow* parent, wxWindowID id, const wxPoint& pos /*= wxDefaultPosition*/, const wxSize& size /*= wxDefaultSize*/)
{
wxWindow::Create(parent, id, pos, size);
SetSize(wxSize(FromDIP(25), FromDIP(25)));
SetMinSize(wxSize(FromDIP(25), FromDIP(25)));
SetMaxSize(wxSize(FromDIP(25), FromDIP(25)));
Bind(wxEVT_PAINT, &ColorPicker::paintEvent, this);
m_bitmap_border = create_scaled_bitmap("color_picker_border", nullptr, 25);
m_bitmap_border_dark = create_scaled_bitmap("color_picker_border_dark", nullptr, 25);
m_bitmap_transparent_def = ScalableBitmap(this, "transparent_color_picker", 25);
m_bitmap_transparent = create_scaled_bitmap("transparent_color_picker", nullptr, 25);
}
ColorPicker::~ColorPicker(){}
void ColorPicker::msw_rescale()
{
m_bitmap_border = create_scaled_bitmap("color_picker_border", nullptr, 25);
m_bitmap_border_dark = create_scaled_bitmap("color_picker_border_dark", nullptr, 25);
m_bitmap_transparent_def.msw_rescale();
Refresh();
}
void ColorPicker::set_color(wxColour col)
{
if (m_colour != col && col.Alpha() != 0 && col.Alpha() != 255 && col.Alpha() != 254) {
transparent_changed = true;
}
m_colour = col;
Refresh();
}
void ColorPicker::set_colors(std::vector<wxColour> cols)
{
m_cols = cols;
Refresh();
}
void ColorPicker::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
render(dc);
}
void ColorPicker::render(wxDC& dc)
{
#ifdef __WXMSW__
wxSize size = GetSize();
wxMemoryDC memdc;
wxBitmap bmp(size.x, size.y);
memdc.SelectObject(bmp);
memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 });
{
wxGCDC dc2(memdc);
doRender(dc2);
}
memdc.SelectObject(wxNullBitmap);
dc.DrawBitmap(bmp, 0, 0);
#else
doRender(dc);
#endif
}
void ColorPicker::doRender(wxDC& dc)
{
wxSize size = GetSize();
auto alpha = m_colour.Alpha();
auto radius = m_show_full ? size.x / 2 - FromDIP(1) : size.x / 2;
if (m_selected) radius -= FromDIP(1);
if (alpha == 0) {
wxSize bmp_size = m_bitmap_transparent_def.GetBmpSize();
int center_x = (size.x - bmp_size.x) / 2;
int center_y = (size.y - bmp_size.y) / 2;
dc.DrawBitmap(m_bitmap_transparent_def.bmp(), center_x, center_y);
}
else if (alpha != 254 && alpha != 255) {
if (transparent_changed) {
std::string rgb = (m_colour.GetAsString(wxC2S_HTML_SYNTAX)).ToStdString();
if (rgb.size() == 9) {
//delete alpha value
rgb = rgb.substr(0, rgb.size() - 2);
}
float alpha_f = 0.7 * m_colour.Alpha() / 255.0;
std::vector<std::string> replace;
replace.push_back(rgb);
std::string fill_replace = "fill-opacity=\"" + std::to_string(alpha_f);
replace.push_back(fill_replace);
m_bitmap_transparent = ScalableBitmap(this, "transparent_color_picker", 25, false, false, true, replace).bmp();
transparent_changed = false;
}
wxSize bmp_size = m_bitmap_transparent.GetSize();
int center_x = (size.x - bmp_size.x) / 2;
int center_y = (size.y - bmp_size.y) / 2;
dc.DrawBitmap(m_bitmap_transparent, center_x, center_y);
}
else {
dc.SetPen(wxPen(m_colour));
dc.SetBrush(wxBrush(m_colour));
dc.DrawCircle(size.x / 2, size.y / 2, radius);
}
if (m_selected) {
dc.SetPen(wxPen(m_colour));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawCircle(size.x / 2, size.y / 2, size.x / 2);
}
if (m_show_full) {
dc.SetPen(wxPen(wxColour("#6B6B6B")));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawCircle(size.x / 2, size.y / 2, radius);
if (m_cols.size() > 1) {
if (ctype == 0) {
int left = FromDIP(0);
float total_width = size.x;
int gwidth = std::round(total_width / (m_cols.size() - 1));
for (int i = 0; i < m_cols.size() - 1; i++) {
if ((left + gwidth) > (size.x)) {
gwidth = size.x - left;
}
auto rect = wxRect(left, 0, gwidth, size.y);
dc.GradientFillLinear(rect, m_cols[i], m_cols[i + 1], wxEAST);
left += gwidth;
}
if (wxGetApp().dark_mode()) {
dc.DrawBitmap(m_bitmap_border_dark, wxPoint(0, 0));
}
else {
dc.DrawBitmap(m_bitmap_border, wxPoint(0, 0));
}
}
else {
float ev_angle = 360.0 / m_cols.size();
float startAngle = 270.0;
float endAngle = 270.0;
dc.SetPen(*wxTRANSPARENT_PEN);
for (int i = 0; i < m_cols.size(); i++) {
dc.SetBrush(m_cols[i]);
endAngle += ev_angle;
endAngle = endAngle > 360.0 ? endAngle - 360.0 : endAngle;
wxPoint center(size.x / 2, size.y / 2);
dc.DrawEllipticArc(center.x - radius, center.y - radius, 2 * radius, 2 * radius, startAngle, endAngle);
startAngle += ev_angle;
startAngle = startAngle > 360.0 ? startAngle - 360.0 : startAngle;
}
if (wxGetApp().dark_mode()) {
dc.DrawBitmap(m_bitmap_border_dark, wxPoint(0, 0));
}
else {
dc.DrawBitmap(m_bitmap_border, wxPoint(0, 0));
}
}
}
}
if (m_is_empty) {
dc.SetTextForeground(*wxBLACK);
auto tsize = dc.GetTextExtent("?");
auto pot = wxPoint((size.x - tsize.x) / 2, (size.y - tsize.y) / 2);
dc.DrawText("?", pot);
}
}
ColorPickerPopup::ColorPickerPopup(wxWindow* parent)
:PopupWindow(parent, wxBORDER_NONE)
{
m_def_colors.clear();
m_def_colors.push_back(wxColour("#FFFFFF"));
m_def_colors.push_back(wxColour("#fff144"));
m_def_colors.push_back(wxColour("#DCF478"));
m_def_colors.push_back(wxColour("#0ACC38"));
m_def_colors.push_back(wxColour("#057748"));
m_def_colors.push_back(wxColour("#0d6284"));
m_def_colors.push_back(wxColour("#0EE2A0"));
m_def_colors.push_back(wxColour("#76D9F4"));
m_def_colors.push_back(wxColour("#46a8f9"));
m_def_colors.push_back(wxColour("#2850E0"));
m_def_colors.push_back(wxColour("#443089"));
m_def_colors.push_back(wxColour("#A03CF7"));
m_def_colors.push_back(wxColour("#F330F9"));
m_def_colors.push_back(wxColour("#D4B1DD"));
m_def_colors.push_back(wxColour("#f95d73"));
m_def_colors.push_back(wxColour("#f72323"));
m_def_colors.push_back(wxColour("#7c4b00"));
m_def_colors.push_back(wxColour("#f98c36"));
m_def_colors.push_back(wxColour("#fcecd6"));
m_def_colors.push_back(wxColour("#D3C5A3"));
m_def_colors.push_back(wxColour("#AF7933"));
m_def_colors.push_back(wxColour("#898989"));
m_def_colors.push_back(wxColour("#BCBCBC"));
m_def_colors.push_back(wxColour("#161616"));
SetBackgroundColour(wxColour(*wxWHITE));
wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL);
wxBoxSizer* m_sizer_box = new wxBoxSizer(wxVERTICAL);
m_def_color_box = new StaticBox(this);
wxBoxSizer* m_sizer_ams = new wxBoxSizer(wxHORIZONTAL);
auto m_title_ams = new wxStaticText(m_def_color_box, wxID_ANY, _L("AMS"), wxDefaultPosition, wxDefaultSize, 0);
m_title_ams->SetFont(::Label::Body_14);
m_title_ams->SetBackgroundColour(wxColour(238, 238, 238));
m_sizer_ams->Add(m_title_ams, 0, wxALL, 5);
auto ams_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
ams_line->SetBackgroundColour(wxColour("#CECECE"));
ams_line->SetMinSize(wxSize(-1, 1));
ams_line->SetMaxSize(wxSize(-1, 1));
m_sizer_ams->Add(ams_line, 1, wxALIGN_CENTER, 0);
m_def_color_box->SetCornerRadius(FromDIP(10));
m_def_color_box->SetBackgroundColor(StateColor(std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Normal)));
m_def_color_box->SetBorderColor(StateColor(std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Normal)));
//ams
m_ams_fg_sizer = new wxFlexGridSizer(0, 8, 0, 0);
m_ams_fg_sizer->SetFlexibleDirection(wxBOTH);
m_ams_fg_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
//other
wxFlexGridSizer* fg_sizer;
fg_sizer = new wxFlexGridSizer(0, 8, 0, 0);
fg_sizer->SetFlexibleDirection(wxBOTH);
fg_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
for (wxColour col : m_def_colors) {
auto cp = new ColorPicker(m_def_color_box, wxID_ANY, wxDefaultPosition, wxDefaultSize);
cp->set_color(col);
cp->set_selected(false);
cp->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(238,238,238)));
m_color_pickers.push_back(cp);
fg_sizer->Add(cp, 0, wxALL, FromDIP(3));
cp->Bind(wxEVT_LEFT_DOWN, [this, cp](auto& e) {
set_def_colour(cp->m_colour);
wxCommandEvent evt(EVT_SELECTED_COLOR);
unsigned long g_col = ((cp->m_colour.Red() & 0xff) << 24) + ((cp->m_colour.Green() & 0xff) << 16) + ((cp->m_colour.Blue() & 0xff) << 8) + (cp->m_colour.Alpha() & 0xff);
evt.SetInt(g_col);
wxPostEvent(GetParent(), evt);
});
}
wxBoxSizer* m_sizer_other = new wxBoxSizer(wxHORIZONTAL);
auto m_title_other = new wxStaticText(m_def_color_box, wxID_ANY, _L("Other Color"), wxDefaultPosition, wxDefaultSize, 0);
m_title_other->SetFont(::Label::Body_14);
m_title_other->SetBackgroundColour(wxColour(238, 238, 238));
m_sizer_other->Add(m_title_other, 0, wxALL, 5);
auto other_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
other_line->SetMinSize(wxSize(-1, 1));
other_line->SetMaxSize(wxSize(-1, 1));
other_line->SetBackgroundColour(wxColour("#CECECE"));
m_sizer_other->Add(other_line, 1, wxALIGN_CENTER, 0);
//custom color
wxBoxSizer* m_sizer_custom = new wxBoxSizer(wxHORIZONTAL);
auto m_title_custom = new wxStaticText(m_def_color_box, wxID_ANY, _L("Custom Color"), wxDefaultPosition, wxDefaultSize, 0);
m_title_custom->SetFont(::Label::Body_14);
m_title_custom->SetBackgroundColour(wxColour(238, 238, 238));
auto custom_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
custom_line->SetBackgroundColour(wxColour("#CECECE"));
custom_line->SetMinSize(wxSize(-1, 1));
custom_line->SetMaxSize(wxSize(-1, 1));
m_sizer_custom->Add(m_title_custom, 0, wxALL, 5);
m_sizer_custom->Add(custom_line, 1, wxALIGN_CENTER, 0);
m_custom_cp = new StaticBox(m_def_color_box);
m_custom_cp->SetSize(FromDIP(60), FromDIP(25));
m_custom_cp->SetMinSize(wxSize(FromDIP(60), FromDIP(25)));
m_custom_cp->SetMaxSize(wxSize(FromDIP(60), FromDIP(25)));
m_custom_cp->SetBorderColor(StateColor(std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Normal)));
m_custom_cp->Bind(wxEVT_LEFT_DOWN, &ColorPickerPopup::on_custom_clr_picker, this);
m_custom_cp->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) {
SetCursor(wxCURSOR_HAND);
});
m_custom_cp->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {
SetCursor(wxCURSOR_ARROW);
});
m_ts_bitmap_custom = ScalableBitmap(this, "ts_custom_color_picker", 25);
m_ts_stbitmap_custom = new wxStaticBitmap(m_custom_cp, wxID_ANY, m_ts_bitmap_custom.bmp());
m_ts_stbitmap_custom->Bind(wxEVT_LEFT_DOWN, &ColorPickerPopup::on_custom_clr_picker, this);
m_ts_stbitmap_custom->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) {
SetCursor(wxCURSOR_HAND);
});
m_ts_stbitmap_custom->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {
SetCursor(wxCURSOR_ARROW);
});
auto sizer_custom = new wxBoxSizer(wxVERTICAL);
m_custom_cp->SetSizer(sizer_custom);
sizer_custom->Add(m_ts_stbitmap_custom, 0, wxEXPAND, 0);
m_custom_cp->Layout();
m_clrData = new wxColourData();
m_clrData->SetChooseFull(true);
m_clrData->SetChooseAlpha(false);
m_sizer_box->Add(0, 0, 0, wxTOP, FromDIP(10));
m_sizer_box->Add(m_sizer_ams, 1, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10));
m_sizer_box->Add(m_ams_fg_sizer, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10));
m_sizer_box->Add(m_sizer_other, 1, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10));
m_sizer_box->Add(fg_sizer, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10));
m_sizer_box->Add(m_sizer_custom, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10));
m_sizer_box->Add(m_custom_cp, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(16));
m_sizer_box->Add(0, 0, 0, wxTOP, FromDIP(10));
m_def_color_box->SetSizer(m_sizer_box);
m_def_color_box->Layout();
m_def_color_box->Fit();
m_sizer_main->Add(m_def_color_box, 0, wxALL | wxEXPAND, 10);
SetSizer(m_sizer_main);
Layout();
Fit();
Bind(wxEVT_PAINT, &ColorPickerPopup::paintEvent, this);
wxGetApp().UpdateDarkUIWin(this);
}
void ColorPickerPopup::on_custom_clr_picker(wxMouseEvent& event)
{
std::vector<std::string> colors = wxGetApp().app_config->get_custom_color_from_config();
for (int i = 0; i < colors.size(); i++) {
m_clrData->SetCustomColour(i, string_to_wxColor(colors[i]));
}
auto clr_dialog = new wxColourDialog(nullptr, m_clrData);
wxColour picker_color;
if (clr_dialog->ShowModal() == wxID_OK) {
m_clrData = &(clr_dialog->GetColourData());
if (colors.size() != CUSTOM_COLOR_COUNT) {
colors.resize(CUSTOM_COLOR_COUNT);
}
for (int i = 0; i < CUSTOM_COLOR_COUNT; i++) {
colors[i] = color_to_string(m_clrData->GetCustomColour(i));
}
wxGetApp().app_config->save_custom_color_to_config(colors);
picker_color = wxColour(
m_clrData->GetColour().Red(),
m_clrData->GetColour().Green(),
m_clrData->GetColour().Blue(),
255
);
if (picker_color.Alpha() == 0) {
m_ts_stbitmap_custom->Show();
}
else {
m_ts_stbitmap_custom->Hide();
m_custom_cp->SetBackgroundColor(picker_color);
}
set_def_colour(picker_color);
wxCommandEvent evt(EVT_SELECTED_COLOR);
unsigned long g_col = ((picker_color.Red() & 0xff) << 24) + ((picker_color.Green() & 0xff) << 16) + ((picker_color.Blue() & 0xff) << 8) + (picker_color.Alpha() & 0xff);
evt.SetInt(g_col);
wxPostEvent(GetParent(), evt);
}
}
void ColorPickerPopup::set_ams_colours(std::vector<wxColour> ams)
{
if (m_ams_color_pickers.size() > 0) {
for (ColorPicker* col_pick:m_ams_color_pickers) {
std::vector<ColorPicker*>::iterator iter = find(m_color_pickers.begin(), m_color_pickers.end(), col_pick);
if (iter != m_color_pickers.end()) {
col_pick->Destroy();
m_color_pickers.erase(iter);
}
}
m_ams_color_pickers.clear();
}
m_ams_colors = ams;
for (wxColour col : m_ams_colors) {
auto cp = new ColorPicker(m_def_color_box, wxID_ANY, wxDefaultPosition, wxDefaultSize);
cp->set_color(col);
cp->set_selected(false);
cp->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(238,238,238)));
m_color_pickers.push_back(cp);
m_ams_color_pickers.push_back(cp);
m_ams_fg_sizer->Add(cp, 0, wxALL, FromDIP(3));
cp->Bind(wxEVT_LEFT_DOWN, [this, cp](auto& e) {
set_def_colour(cp->m_colour);
wxCommandEvent evt(EVT_SELECTED_COLOR);
unsigned long g_col = ((cp->m_colour.Red() & 0xff) << 24) + ((cp->m_colour.Green() & 0xff) << 16) + ((cp->m_colour.Blue() & 0xff) << 8) + (cp->m_colour.Alpha() & 0xff);
evt.SetInt(g_col);
wxPostEvent(GetParent(), evt);
});
}
m_ams_fg_sizer->Layout();
Layout();
Fit();
}
void ColorPickerPopup::set_def_colour(wxColour col)
{
m_def_col = col;
for (ColorPicker* cp : m_color_pickers) {
if (cp->m_selected) {
cp->set_selected(false);
}
}
for (ColorPicker* cp : m_color_pickers) {
if (cp->m_colour == m_def_col) {
cp->set_selected(true);
break;
}
}
if (m_def_col.Alpha() == 0) {
m_ts_stbitmap_custom->Show();
}
else {
m_ts_stbitmap_custom->Hide();
m_custom_cp->SetBackgroundColor(m_def_col);
}
Dismiss();
}
void ColorPickerPopup::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
}
void ColorPickerPopup::OnDismiss() {}
bool ColorPickerPopup::ProcessLeftDown(wxMouseEvent& event) {
return PopupWindow::ProcessLeftDown(event);
}
}} // namespace Slic3r::GUI