ENH: refine ui logic with filament group

1. Add filament group pop up when slice
2. Add more filament modes in filament dialog
3. Add capsule button

jira:NONE

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I8bc3a2e08696e406b89e550a0335a1a36728ee65
(cherry picked from commit f1702a5c3604f685a3b35ea0e83d29bdbbd90f70)
This commit is contained in:
xun.zhang
2024-11-29 17:37:07 +08:00
committed by Noisyfox
parent 973c2f9cf3
commit 057a1a4f5d
27 changed files with 1258 additions and 276 deletions

View File

@@ -302,6 +302,18 @@ void AppConfig::set_defaults()
set_bool("auto_calculate_when_filament_change", true);
}
if (get("ignore_ext_filament_in_filament_map").empty()){
set_bool("ignore_ext_filament_in_filament_map", false);
}
if (get("pop_up_filament_map_mode").empty()){
set_bool("pop_up_filament_map_mode", true);
}
if (get("prefered_filament_map_mode").empty()){
set("prefered_filament_map_mode",ConfigOptionEnum<FilamentMapMode>::get_enum_names()[FilamentMapMode::fmmAutoForFlush]);
}
if (get("show_home_page").empty()) {
set_bool("show_home_page", true);
}

View File

@@ -301,6 +301,10 @@ set(SLIC3R_GUI_SOURCES
GUI/Field.hpp
GUI/DragDropPanel.cpp
GUI/DragDropPanel.hpp
GUI/CapsuleButton.cpp
GUI/CapsuleButton.hpp
GUI/FilamentMapPanel.cpp
GUI/FilamentMapPanel.hpp
GUI/FilamentMapDialog.cpp
GUI/FilamentMapDialog.hpp
GUI/FileArchiveDialog.cpp
@@ -430,6 +434,8 @@ set(SLIC3R_GUI_SOURCES
GUI/InstanceCheck.hpp
GUI/Search.cpp
GUI/Search.hpp
GUI/FilamentGroupPopup.hpp
GUI/FilamentGroupPopup.cpp
GUI/NotificationManager.cpp
GUI/NotificationManager.hpp
GUI/UnsavedChangesDialog.cpp

View File

@@ -0,0 +1,117 @@
#include "GUI_App.hpp"
#include "CapsuleButton.hpp"
#include "wx/graphics.h"
#include "Widgets/Label.hpp"
#include "wx/dcgraph.h"
namespace Slic3r { namespace GUI {
CapsuleButton::CapsuleButton(wxWindow *parent, wxWindowID id, const wxString &label, bool selected) : wxPanel(parent, id)
{
SetBackgroundColour(*wxWHITE);
SetBackgroundStyle(wxBG_STYLE_PAINT);
m_hovered = false;
m_selected = selected;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
std::string icon_name = selected ? "capsule_tag_on" : "capsule_tag_off";
auto bmp = create_scaled_bitmap(icon_name, nullptr, FromDIP(16));
m_btn = new wxBitmapButton(this, wxID_ANY, bmp, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
m_btn->SetBackgroundColour(*wxWHITE);
m_label = new Label(this, label);
sizer->AddSpacer(FromDIP(8));
sizer->Add(m_btn, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(6));
sizer->AddSpacer(FromDIP(8));
sizer->Add(m_label, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(6));
sizer->AddSpacer(FromDIP(8));
SetSizer(sizer);
Layout();
Fit();
auto forward_click_to_parent = [this](auto &event) {
wxCommandEvent click_event(wxEVT_BUTTON, GetId());
click_event.SetEventObject(this);
this->ProcessEvent(click_event);
};
m_btn->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
m_label->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
this->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
Bind(wxEVT_PAINT, &CapsuleButton::OnPaint, this);
Bind(wxEVT_ENTER_WINDOW, &CapsuleButton::OnEnterWindow, this);
Bind(wxEVT_LEAVE_WINDOW, &CapsuleButton::OnLeaveWindow, this);
GUI::wxGetApp().UpdateDarkUIWin(this);
}
void CapsuleButton::OnPaint(wxPaintEvent &event)
{
wxAutoBufferedPaintDC dc(this);
wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
if (gc) {
dc.Clear();
wxRect rect = GetClientRect();
gc->SetBrush(wxTransparentColour);
gc->DrawRoundedRectangle(0, 0, rect.width, rect.height, 0);
wxColour bg_color = m_selected ? wxColour("#EBF9F0") : wxColour("#FFFFFF");
wxColour border_color = m_hovered || m_selected ? wxColour("#00AE42") : wxColour("#CECECE");
int cornerRadius = 5;
gc->SetBrush(wxBrush(bg_color));
gc->SetPen(wxPen(border_color, 2));
gc->DrawRoundedRectangle(1, 1, rect.width - 2, rect.height - 2, cornerRadius);
delete gc;
}
}
void CapsuleButton::Select(bool selected)
{
m_selected = selected;
UpdateStatus();
Refresh();
}
void CapsuleButton::OnEnterWindow(wxMouseEvent &event)
{
if (!m_hovered) {
m_hovered = true;
UpdateStatus();
Refresh();
}
event.Skip();
}
void CapsuleButton::OnLeaveWindow(wxMouseEvent &event)
{
if (m_hovered) {
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
m_hovered = false;
UpdateStatus();
Refresh();
}
event.Skip();
}
void CapsuleButton::UpdateStatus()
{
const wxColour selected_color = wxColour("#EBF9F0");
const wxColour normal_color = wxColour("#FFFFFF");
std::string icon_name = m_selected ? "capsule_tag_on" : "capsule_tag_off";
auto bmp = create_scaled_bitmap(icon_name, nullptr, FromDIP(16));
m_btn->SetBitmap(bmp);
if (m_selected) {
m_label->SetBackgroundColour(selected_color);
m_btn->SetBackgroundColour(selected_color);
} else {
m_label->SetBackgroundColour(normal_color);
m_btn->SetBackgroundColour(normal_color);
}
}
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,28 @@
#ifndef CAPSULE_BUTTON_HPP
#define CAPSULE_BUTTON_HPP
#include "GUI.hpp"
#include "wxExtensions.hpp"
namespace Slic3r { namespace GUI {
class CapsuleButton : public wxPanel
{
public:
CapsuleButton(wxWindow *parent, wxWindowID id, const wxString &label, bool selected);
void Select(bool selected);
protected:
void OnPaint(wxPaintEvent &event);
private:
void OnEnterWindow(wxMouseEvent &event);
void OnLeaveWindow(wxMouseEvent &event);
void UpdateStatus();
wxBitmapButton *m_btn;
Label *m_label;
bool m_hovered;
bool m_selected;
};
}} // namespace Slic3r::GUI
#endif

View File

@@ -1,4 +1,5 @@
#include "DragDropPanel.hpp"
#include "Widgets/Label.hpp"
#include <slic3r/GUI/wxExtensions.hpp>
namespace Slic3r { namespace GUI {
@@ -9,6 +10,27 @@ struct CustomData
unsigned char r, g, b;
};
wxColor Hex2Color(const std::string& str)
{
if (str.empty() || (str.length() != 9 && str.length() != 7) || str[0] != '#')
throw std::invalid_argument("Invalid hex color format");
auto hexToByte = [](const std::string& hex)->unsigned char
{
unsigned int byte;
std::istringstream(hex) >> std::hex >> byte;
return static_cast<unsigned char>(byte);
};
auto r = hexToByte(str.substr(1, 2));
auto g = hexToByte(str.substr(3, 2));
auto b = hexToByte(str.substr(5, 2));
unsigned char a = 255;
if (str.size() == 9)
a = hexToByte(str.substr(7, 2));
return wxColor(r, g, b, a);
}
// Custom data object used to store information that needs to be backed up during drag and drop
class ColorDataObject : public wxCustomDataObject
{
@@ -150,21 +172,24 @@ DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_au
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
title_panel->SetSizer(title_sizer);
wxStaticText *staticText = new wxStaticText(this, wxID_ANY, label);
staticText->SetBackgroundColour(0xEEEEEE);
title_sizer->Add(staticText, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
Label* static_text = new Label(this, label);
static_text->SetFont(Label::Head_13);
static_text->SetBackgroundColour(0xEEEEEE);
title_sizer->Add(static_text, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
m_sizer->Add(title_panel, 0, wxEXPAND);
m_sizer->AddSpacer(20);
m_grid_item_sizer = new wxGridSizer(0, 5, FromDIP(10), 0); // row = 0, col = 3, 10 10 is space
m_sizer->Add(m_grid_item_sizer);
m_grid_item_sizer = new wxGridSizer(0, 6, FromDIP(5),FromDIP(5)); // row = 0, col = 3, 10 10 is space
m_sizer->Add(m_grid_item_sizer, 1, wxEXPAND);
// set droptarget
auto drop_target = new ColorDropTarget(this);
SetDropTarget(drop_target);
SetSizer(m_sizer);
Layout();
Fit();
}
@@ -172,7 +197,7 @@ void DragDropPanel::AddColorBlock(const wxColour &color, int filament_id, bool u
{
ColorPanel *panel = new ColorPanel(this, color, filament_id);
panel->SetMinSize(wxSize(FromDIP(32), FromDIP(40)));
m_grid_item_sizer->Add(panel, 0, wxALIGN_CENTER | wxLEFT, FromDIP(10));
m_grid_item_sizer->Add(panel, 0, wxALIGN_CENTER);
m_filament_blocks.push_back(panel);
if (update_ui) {
m_filament_blocks.front()->Refresh(); // FIX BUG: STUDIO-8467

View File

@@ -11,6 +11,9 @@
namespace Slic3r { namespace GUI {
wxColor Hex2Color(const std::string& str);
class ColorPanel;
class DragDropPanel : public wxPanel
{

View File

@@ -0,0 +1,318 @@
#include "FilamentGroupPopup.hpp"
#include "FilamentMapDialog.hpp"
#include "GUI_App.hpp"
#include "MsgDialog.hpp"
#include "I18N.hpp"
#include "wx/dcgraph.h"
namespace Slic3r { namespace GUI {
static const wxString AutoForFlushLabel = _L("Filament-Saving Mode");
static const wxString AutoForMatchLabel = _L("Convenient Mode");
static const wxString ManualLabel = _L("Manual Mode");
static const wxString AutoForFlushDesp = _L("(Arrage after slicing)");
static const wxString AutoForMatchDesp = _L("(Arrange before slicing)");
static const wxString ManualDesp = "";
static const wxString AutoForFlushDetail = _L("Disregrad the filaments in AMS. Optimize filament usage "
"by calculating the best allocation for the left and right "
"nozzles. Arrange the filaments according on the printer according to "
"the slicing results.");
static const wxString AutoForMatchDetail = _L("Based on the current filaments in the AMS, allocate the "
"filaments to the left and right nozzles.");
static const wxString ManualDetail = _L("Mannully allocate the filaments for the left and right nozzles.");
static bool is_multi_extruder()
{
const auto &preset_bundle = wxGetApp().preset_bundle;
const auto &full_config = preset_bundle->full_config();
const auto nozzle_diameters = full_config.option<ConfigOptionFloatsNullable>("nozzle_diameter");
return nozzle_diameters->size() > 1;
}
FilamentMapMode get_prefered_map_mode()
{
const static std::map<std::string, int> enum_keys_map = ConfigOptionEnum<FilamentMapMode>::get_enum_values();
auto &app_config = wxGetApp().app_config;
std::string mode_str = app_config->get("prefered_filament_map_mode");
auto iter = enum_keys_map.find(mode_str);
if (iter == enum_keys_map.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("Could not get prefered_filament_map_mode from app config, use AutoForFlsuh mode");
return FilamentMapMode::fmmAutoForFlush;
}
return FilamentMapMode(iter->second);
}
static void set_prefered_map_mode(FilamentMapMode mode)
{
const static std::vector<std::string> enum_values = ConfigOptionEnum<FilamentMapMode>::get_enum_names();
auto &app_config = wxGetApp().app_config;
std::string mode_str;
if (mode < enum_values.size()) mode_str = enum_values[mode];
if (mode_str.empty()) BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("Set empty prefered_filament_map_mode to app config");
app_config->set("prefered_filament_map_mode", mode_str);
}
static bool get_pop_up_remind_flag()
{
auto &app_config = wxGetApp().app_config;
return app_config->get_bool("pop_up_filament_map_mode");
}
static void set_pop_up_remind_flag(bool remind)
{
auto &app_config = wxGetApp().app_config;
app_config->set_bool("pop_up_filament_map_mode", remind);
}
bool is_pop_up_required()
{
FilamentMapMode mode = get_prefered_map_mode();
bool is_manual_mode_ = FilamentMapMode::fmmManual == mode;
bool is_multi_extruder_ = is_multi_extruder();
return is_multi_extruder_ && is_manual_mode_;
}
FilamentGroupPopup::FilamentGroupPopup(wxWindow *parent) : PopupWindow(parent, wxBORDER_NONE)
{
wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
const int horizontal_margin = FromDIP(16);
const int vertical_margin = FromDIP(15);
const int vertical_padding = FromDIP(12);
const int ratio_spacing = FromDIP(4);
const wxColor background_color = wxColor(255, 255, 255);
SetBackgroundColour(background_color);
radio_btns.resize(ButtonType::btCount);
button_labels.resize(ButtonType::btCount);
button_desps.resize(ButtonType::btCount);
detail_infos.resize(ButtonType::btCount);
std::vector<wxString> btn_texts = {AutoForFlushLabel, AutoForMatchLabel, ManualLabel};
std::vector<wxString> btn_desps = {AutoForFlushDesp, AutoForMatchDesp, ManualDesp};
std::vector<wxString> mode_details = {AutoForFlushDetail, AutoForMatchDetail, ManualDetail};
top_sizer->AddSpacer(vertical_margin);
auto checked_bmp = create_scaled_bitmap("map_mode_on", nullptr, 16);
auto unchecked_bmp = create_scaled_bitmap("map_mode_off", nullptr, 16);
for (size_t idx = 0; idx < ButtonType::btCount; ++idx) {
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
radio_btns[idx] = new wxBitmapButton(this, idx, unchecked_bmp, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
radio_btns[idx]->SetBackgroundColour(background_color);
button_labels[idx] = new wxStaticText(this, wxID_ANY, btn_texts[idx]);
button_labels[idx]->SetBackgroundColour(background_color);
button_labels[idx]->SetForegroundColour(wxColor("#262E30"));
button_labels[idx]->SetFont(Label::Head_14);
button_desps[idx] = new wxStaticText(this, wxID_ANY, btn_desps[idx]);
button_desps[idx]->SetBackgroundColour(background_color);
button_desps[idx]->SetForegroundColour(wxColor("#262E30"));
button_desps[idx]->SetFont(Label::Body_14);
button_sizer->Add(radio_btns[idx], 0, wxALIGN_CENTER_VERTICAL);
button_sizer->AddSpacer(ratio_spacing);
button_sizer->Add(button_labels[idx], 0, wxALIGN_CENTER_VERTICAL);
button_sizer->Add(button_desps[idx], 0, wxALIGN_CENTER_VERTICAL);
wxBoxSizer *label_sizer = new wxBoxSizer(wxHORIZONTAL);
detail_infos[idx] = new wxStaticText(this, wxID_ANY, mode_details[idx]);
detail_infos[idx]->SetBackgroundColour(background_color);
detail_infos[idx]->SetForegroundColour(wxColor("#6B6B6B"));
detail_infos[idx]->SetFont(Label::Body_12);
detail_infos[idx]->Wrap(FromDIP(320));
label_sizer->AddSpacer(radio_btns[idx]->GetRect().width + ratio_spacing);
label_sizer->Add(detail_infos[idx], 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
top_sizer->Add(button_sizer, 0, wxLEFT | wxRIGHT, horizontal_margin);
top_sizer->Add(label_sizer, 0, wxLEFT | wxRIGHT, horizontal_margin);
top_sizer->AddSpacer(vertical_padding);
radio_btns[idx]->Bind(wxEVT_BUTTON, [this, idx](wxCommandEvent &event) {
event.SetInt(idx);
OnRadioBtn(event);
});
radio_btns[idx]->Bind(wxEVT_ENTER_WINDOW, [this, idx](wxMouseEvent &) { UpdateButtonStatus(idx); });
radio_btns[idx]->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &) { UpdateButtonStatus(); });
button_labels[idx]->Bind(wxEVT_LEFT_UP, [this, idx](wxMouseEvent &) {
wxCommandEvent event;
event.SetInt(idx);
OnRadioBtn(event);
});
button_labels[idx]->Bind(wxEVT_ENTER_WINDOW, [this, idx](wxMouseEvent &) { UpdateButtonStatus(idx); });
button_labels[idx]->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &) { UpdateButtonStatus(); });
}
{
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
wiki_link = new wxStaticText(this, wxID_ANY, _L("More info on wiki"));
wiki_link->SetBackgroundColour(background_color);
wiki_link->SetForegroundColour(wxColor("#00AE42"));
wiki_link->SetFont(Label::Body_12.Underlined());
wiki_link->SetCursor(wxCursor(wxCURSOR_HAND));
wiki_link->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent &) { wxLaunchDefaultBrowser("http//:example.com"); });
remind_checkbox = new wxCheckBox(this, wxID_ANY, _L("Don't remind me again"));
remind_checkbox->SetBackgroundColour(background_color);
remind_checkbox->SetForegroundColour(wxColor("#6B6B6B"));
remind_checkbox->SetFont(Label::Body_12);
remind_checkbox->Bind(wxEVT_CHECKBOX, &FilamentGroupPopup::OnRemindBtn, this);
button_sizer->Add(wiki_link, 0, wxLEFT, horizontal_margin);
button_sizer->AddStretchSpacer();
button_sizer->Add(remind_checkbox, 0, wxRIGHT, horizontal_margin);
top_sizer->Add(button_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, horizontal_margin);
}
top_sizer->AddSpacer(vertical_margin);
SetSizerAndFit(top_sizer);
m_mode = get_prefered_map_mode();
m_timer = new wxTimer(this);
GUI::wxGetApp().UpdateDarkUIWin(this);
Bind(wxEVT_TIMER, &FilamentGroupPopup::OnTimer, this);
Bind(wxEVT_ENTER_WINDOW, &FilamentGroupPopup::OnEnterWindow, this);
Bind(wxEVT_LEAVE_WINDOW, &FilamentGroupPopup::OnLeaveWindow, this);
}
void FilamentGroupPopup::DrawRoundedCorner(int radius)
{
#ifdef __WIN32__
HWND hwnd = GetHWND();
if (hwnd) {
HRGN hrgn = CreateRoundRectRgn(0, 0, GetRect().GetWidth(), GetRect().GetHeight(), radius, radius);
SetWindowRgn(hwnd, hrgn, TRUE);
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hwnd, 0, 0, LWA_COLORKEY);
}
#else
wxClientDC dc(this);
wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
if (gc) {
gc->SetBrush(*wxWHITE_BRUSH);
gc->SetPen(*wxTRANSPARENT_PEN);
wxRect rect(0, 0, GetSize().GetWidth(), GetSize().GetHeight());
wxGraphicsPath path = wxGraphicsRenderer::GetDefaultRenderer()->CreatePath();
path.AddRoundedRectangle(0, 0, rect.width, rect.height, radius);
gc->DrawPath(path);
delete gc;
}
#endif
}
void FilamentGroupPopup::Init(bool connect_status)
{
radio_btns[ButtonType::btForMatch]->Enable(connect_status);
button_labels[ButtonType::btForMatch]->Enable(connect_status);
m_mode = get_prefered_map_mode();
wxCheckBoxState check_state = get_pop_up_remind_flag() ? wxCheckBoxState::wxCHK_UNCHECKED : wxCheckBoxState::wxCHK_CHECKED;
remind_checkbox->Set3StateValue(check_state);
UpdateButtonStatus();
}
void FilamentGroupPopup::tryPopup(bool connect_status)
{
auto canPopup = [this]() {
bool is_multi_extruder_ = is_multi_extruder();
bool pop_up_flag = get_pop_up_remind_flag();
return is_multi_extruder_ && pop_up_flag;
};
if (canPopup()) {
DrawRoundedCorner(16);
Init(connect_status);
ResetTimer();
PopupWindow::Popup();
}
}
void FilamentGroupPopup::tryClose() { StartTimer(); }
void FilamentGroupPopup::StartTimer() { m_timer->StartOnce(300); }
void FilamentGroupPopup::ResetTimer()
{
if (m_timer->IsRunning()) { m_timer->Stop(); }
}
void FilamentGroupPopup::OnRadioBtn(wxCommandEvent &event)
{
m_mode = mode_list.at(event.GetInt());
set_prefered_map_mode(m_mode);
UpdateButtonStatus(m_mode);
}
void FilamentGroupPopup::OnRemindBtn(wxCommandEvent &event)
{
bool is_checked = remind_checkbox->IsChecked();
set_pop_up_remind_flag(!is_checked);
if (is_checked) {
MessageDialog dialog(nullptr, _L("No further pop up.You can go to \"Preferences\" to reopen the pop up."), _L("Tips"), wxICON_INFORMATION | wxOK);
dialog.ShowModal();
Dismiss();
}
}
void FilamentGroupPopup::OnTimer(wxTimerEvent &event) { Dismiss(); }
void FilamentGroupPopup::OnLeaveWindow(wxMouseEvent &)
{
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
StartTimer();
}
void FilamentGroupPopup::OnEnterWindow(wxMouseEvent &) { ResetTimer(); }
void FilamentGroupPopup::UpdateButtonStatus(int hover_idx)
{
auto checked_bmp = create_scaled_bitmap("map_mode_on", nullptr, 16);
auto unchecked_bmp = create_scaled_bitmap("map_mode_off", nullptr, 16);
auto checked_hover_bmp = create_scaled_bitmap("map_mode_on_hovered", nullptr);
auto unchecked_hover_bmp = create_scaled_bitmap("map_mode_off_hovered", nullptr);
for (int i = 0; i < ButtonType::btCount; ++i) {
// process checked and unchecked status
if (mode_list.at(i) == m_mode) {
if (i == hover_idx)
radio_btns[i]->SetBitmap(checked_hover_bmp);
else
radio_btns[i]->SetBitmap(checked_bmp);
button_labels[i]->SetFont(Label::Head_14);
} else {
if (i == hover_idx)
radio_btns[i]->SetBitmap(unchecked_hover_bmp);
else
radio_btns[i]->SetBitmap(unchecked_bmp);
button_labels[i]->SetFont(Label::Body_14);
}
}
if (m_mode == FilamentMapMode::fmmAutoForMatch)
remind_checkbox->Enable(false);
else
remind_checkbox->Enable(true);
}
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,54 @@
#ifndef FILAMENT_GROUP_HOVER_HPP
#define FILAMENT_GROUP_HOVER_HPP
#include <wx/graphics.h>
#include <wx/hyperlink.h>
#include "wxExtensions.hpp"
#include "Widgets/PopupWindow.hpp"
namespace Slic3r { namespace GUI {
bool is_pop_up_required();
FilamentMapMode get_prefered_map_mode();
class FilamentGroupPopup : public PopupWindow
{
public:
FilamentGroupPopup(wxWindow *parent);
void tryPopup(bool connect_status);
void tryClose();
FilamentMapMode GetSelectedMode() const { return m_mode; }
private:
void StartTimer();
void ResetTimer();
void OnRadioBtn(wxCommandEvent &event);
void OnLeaveWindow(wxMouseEvent &);
void OnEnterWindow(wxMouseEvent &);
void OnTimer(wxTimerEvent &event);
void OnRemindBtn(wxCommandEvent &event);
void Init(bool connect_status);
void UpdateButtonStatus(int hover_idx = -1);
void DrawRoundedCorner(int radius);
private:
enum ButtonType { btForFlush, btForMatch, btManual, btCount };
const std::vector<FilamentMapMode> mode_list = {fmmAutoForFlush, fmmAutoForMatch, fmmManual};
FilamentMapMode m_mode;
wxTimer *m_timer;
std::vector<wxBitmapButton *> radio_btns;
std::vector<wxStaticText *> button_labels;
std::vector<wxStaticText *> button_desps;
std::vector<wxStaticText *> detail_infos;
wxStaticText *wiki_link;
wxCheckBox *remind_checkbox;
};
}} // namespace Slic3r::GUI
#endif

View File

@@ -3,210 +3,118 @@
#include "Widgets/Button.hpp"
#include "Widgets/SwitchButton.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "CapsuleButton.hpp"
#include "SelectMachine.hpp"
namespace Slic3r { namespace GUI {
const wxString manual_tips = _L("we will slice according to this grouping method:");
const wxString manual_below_tips = _L("Tips:\n"
"You can drag the filaments to change which extruder they are assigned to.\n"
"But your filament arrangement may not be the most filament-efficient.");
const wxString auto_tips = _L("Automatic filament grouping will be performed to reduce flushing consumption\n"
"and filament changes during the slicing process.\n"
"The recommended results will be displayed below after slicing:");
const wxString auto_tips_with_result = _L("Automatic filament grouping will be performed to reduce flushing consumption\n"
"and filament changes during the slicing process.\n"
"The recommended results are shown below:");
wxColour hex_to_color(const std::string &hex)
{
if ((hex.length() != 7 && hex.length() != 9) || hex[0] != '#') {
throw std::invalid_argument("Invalid hex color format");
}
unsigned int r, g, b, a = 255;
std::stringstream ss;
// r
ss << std::hex << hex.substr(1, 2);
ss >> r;
ss.clear();
ss.str("");
// g
ss << std::hex << hex.substr(3, 2);
ss >> g;
ss.clear();
ss.str("");
// b
ss << std::hex << hex.substr(5, 2);
ss >> b;
// a
if (hex.length() == 9) {
ss.clear();
ss.str("");
ss << std::hex << hex.substr(7, 2);
ss >> a;
}
return wxColour(r, g, b, a);
}
FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
const DynamicPrintConfig *config,
const std::vector<int> &filament_map,
const std::vector<int> &extruders,
bool is_auto,
bool has_auto_result
)
FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
const std::vector<std::string> &filament_color,
const std::vector<int> &filament_map,
const std::vector<int> &filaments,
const FilamentMapMode mode,
bool show_default)
: wxDialog(parent, wxID_ANY, _L("Filament arrangement method of plate"), wxDefaultPosition, wxSize(2000, 1500))
, m_config(config)
, m_filament_color(filament_color)
, m_filament_map(filament_map)
, m_has_auto_result(has_auto_result)
{
SetBackgroundColour(*wxWHITE);
SetMinSize(wxSize(FromDIP(550), -1));
SetMaxSize(wxSize(FromDIP(550), -1));
if (mode < fmmManual)
m_page_type = PageType::ptAuto;
else if (mode == fmmManual)
m_page_type = PageType::ptManual;
else
m_page_type = PageType::ptDefault;
wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->AddSpacer(FromDIP(22));
m_mode_switch_btn = new SwitchButton(this);
m_mode_switch_btn->SetMaxSize({em_unit(this) * 12, -1});
m_mode_switch_btn->SetLabels(_L("Customize"), _L("Auto"));
m_mode_switch_btn->Bind(wxEVT_TOGGLEBUTTON, &FilamentMapDialog::on_switch_mode, this);
m_mode_switch_btn->SetValue(is_auto);
main_sizer->Add(m_mode_switch_btn, 0, wxCENTER | wxALL, 10);
wxBoxSizer *mode_sizer = new wxBoxSizer(wxHORIZONTAL);
m_tip_text = new wxStaticText(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
main_sizer->Add(m_tip_text, 0, wxALIGN_LEFT | wxLEFT, 15);
m_auto_btn = new CapsuleButton(this, PageType::ptAuto, _L("Auto"), false);
m_manual_btn = new CapsuleButton(this, PageType::ptManual, _L("Custom"), false);
if (show_default)
m_default_btn = new CapsuleButton(this, PageType::ptDefault, _L("Default"), true);
else
m_default_btn = nullptr;
m_extruder_panel_sizer = new wxBoxSizer(wxHORIZONTAL);
const int button_padding = FromDIP(2);
mode_sizer->AddStretchSpacer();
mode_sizer->Add(m_auto_btn, 1, wxALIGN_CENTER | wxLEFT | wxRIGHT, button_padding);
mode_sizer->Add(m_manual_btn, 1, wxALIGN_CENTER | wxLEFT | wxRIGHT, button_padding);
if (show_default) mode_sizer->Add(m_default_btn, 1, wxALIGN_CENTER | wxLEFT | wxRIGHT, button_padding);
mode_sizer->AddStretchSpacer();
m_manual_left_panel = new DragDropPanel(this, wxT("Left nozzle:"), false);
m_manual_right_panel = new DragDropPanel(this, wxT("Right nozzle:"), false);
main_sizer->Add(mode_sizer, 0, wxEXPAND);
main_sizer->AddSpacer(FromDIP(24));
std::vector<std::string> filament_color = config->option<ConfigOptionStrings>("filament_colour")->values;
for (size_t i = 0; i < filament_map.size(); ++i) {
auto iter = std::find(extruders.begin(), extruders.end(), i + 1);
if (iter == extruders.end())
continue;
auto panel_sizer = new wxBoxSizer(wxHORIZONTAL);
FilamentMapMode default_auto_mode = (mode < FilamentMapMode::fmmManual ? mode : FilamentMapMode::fmmAutoForFlush);
m_manual_map_panel = new FilamentMapManualPanel(this, m_filament_color, filaments, filament_map);
m_auto_map_panel = new FilamentMapAutoPanel(this, default_auto_mode);
if (show_default)
m_default_map_panel = new FilamentMapDefaultPanel(this);
else
m_default_map_panel = nullptr;
if (filament_map[i] == 1) {
m_manual_left_panel->AddColorBlock(hex_to_color(filament_color[i]), i + 1);
}
else if (filament_map[i] == 2) {
m_manual_right_panel->AddColorBlock(hex_to_color(filament_color[i]), i + 1);
}
else {
assert(false);
}
}
panel_sizer->Add(m_manual_map_panel, 0, wxALIGN_CENTER | wxEXPAND);
panel_sizer->Add(m_auto_map_panel, 0, wxALIGN_CENTER | wxEXPAND);
if (show_default) panel_sizer->Add(m_default_map_panel, 0, wxALIGN_CENTER | wxEXPAND);
main_sizer->Add(panel_sizer, 0, wxEXPAND);
m_switch_filament_btn = new ScalableButton(this, wxID_ANY, "switch_filament_maps");
m_switch_filament_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_filaments, this);
m_switch_filament_btn->SetCanFocus(false);
// just for placeholder for auto
m_switch_filament_btn_auto = new ScalableButton(this, wxID_ANY, "switch_filament_maps");
m_switch_filament_btn_auto->Enable(false);
m_extruder_panel_sizer->Add(m_manual_left_panel, 1, wxEXPAND | wxALL, 5);
m_extruder_panel_sizer->Add(m_switch_filament_btn, 0, wxALIGN_CENTER_VERTICAL | wxALL, 1);
m_extruder_panel_sizer->Add(m_manual_right_panel, 1, wxEXPAND | wxALL, 5);
m_manual_left_panel->Layout();
m_manual_left_panel->Fit();
m_manual_right_panel->Layout();
m_manual_right_panel->Fit();
m_auto_left_panel = new DragDropPanel(this, wxT("Left nozzle:"), true);
m_auto_right_panel = new DragDropPanel(this, wxT("Right nozzle:"), true);
for (size_t i = 0; i < filament_map.size(); ++i) {
auto iter = std::find(extruders.begin(), extruders.end(), i + 1);
if (iter == extruders.end()) continue;
if (filament_map[i] == 1) {
m_auto_left_panel->AddColorBlock(hex_to_color(filament_color[i]), i + 1);
} else if (filament_map[i] == 2) {
m_auto_right_panel->AddColorBlock(hex_to_color(filament_color[i]), i + 1);
} else {
assert(false);
}
}
m_extruder_panel_sizer->Add(m_auto_left_panel, 1, wxEXPAND | wxALL, 5);
m_extruder_panel_sizer->Add(m_switch_filament_btn_auto, 0, wxALIGN_CENTER_VERTICAL | wxALL, 1);
m_extruder_panel_sizer->Add(m_auto_right_panel, 1, wxEXPAND | wxALL, 5);
m_auto_left_panel->Layout();
m_auto_left_panel->Fit();
m_auto_right_panel->Layout();
m_auto_right_panel->Fit();
main_sizer->Add(m_extruder_panel_sizer, 1, wxEXPAND | wxALL, 10);
m_below_tip_text = new wxStaticText(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
m_below_tip_text->SetLabel(manual_below_tips);
main_sizer->Add(m_below_tip_text, 0, wxALIGN_LEFT | wxLEFT, 15);
if (is_auto) {
m_manual_left_panel->Hide();
m_manual_right_panel->Hide();
m_switch_filament_btn->Hide();
m_below_tip_text->Hide();
if (m_has_auto_result) {
m_tip_text->SetLabel(auto_tips_with_result);
}
else {
m_auto_left_panel->Hide();
m_auto_right_panel->Hide();
m_switch_filament_btn_auto->Hide();
m_tip_text->SetLabel(auto_tips);
}
}
else {
m_auto_left_panel->Hide();
m_auto_right_panel->Hide();
m_switch_filament_btn_auto->Hide();
m_tip_text->SetLabel(manual_tips);
m_below_tip_text->Show();
}
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
Button * ok_btn = new Button(this, _L("OK"));
Button * cancel_btn = new Button(this, _L("Cancel"));
button_sizer->Add(ok_btn, 0, wxALL, 5);
button_sizer->Add(cancel_btn, 0, wxALL, 5);
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
m_ok_btn = new Button(this, _L("OK"));
m_cancel_btn = new Button(this, _L("Cancel"));
button_sizer->Add(m_ok_btn, 0, wxALL, 5);
button_sizer->Add(m_cancel_btn, 0, wxALL, 5);
main_sizer->Add(button_sizer, 0, wxALIGN_CENTER | wxALL, 10);
ok_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_ok, this);
cancel_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_cancle, this);
m_ok_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_ok, this);
m_cancel_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_cancle, this);
m_auto_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
m_manual_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
if (show_default) m_default_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
SetSizer(main_sizer);
Layout();
Fit();
CenterOnParent();
GUI::wxGetApp().UpdateDarkUIWin(this);
}
bool FilamentMapDialog::is_auto() const
FilamentMapMode FilamentMapDialog::get_mode()
{
if (m_mode_switch_btn->GetValue()) {
return true;
}
return false;
if (m_page_type == PageType::ptAuto)
return m_auto_map_panel->GetMode();
if (m_page_type == PageType::ptManual)
return fmmManual;
return fmmDefault;
}
int FilamentMapDialog::ShowModal()
{
update_panel_status(m_page_type);
return wxDialog::ShowModal();
}
void FilamentMapDialog::on_ok(wxCommandEvent &event)
{
if (!is_auto()) {
std::vector<int> left_filaments = m_manual_left_panel->GetAllFilaments();
std::vector<int> right_filaments = m_manual_right_panel->GetAllFilaments();
if (m_page_type == PageType::ptManual) {
std::vector<int> left_filaments = m_manual_map_panel->GetLeftFilaments();
std::vector<int> right_filaments = m_manual_map_panel->GetRightFilaments();
for (int i = 0; i < m_filament_map.size(); ++i) {
if (std::find(left_filaments.begin(), left_filaments.end(), i + 1) != left_filaments.end()) {
m_filament_map[i] = 1;
} else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) {
}
else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) {
m_filament_map[i] = 2;
}
}
@@ -215,63 +123,62 @@ void FilamentMapDialog::on_ok(wxCommandEvent &event)
EndModal(wxID_OK);
}
void FilamentMapDialog::on_cancle(wxCommandEvent &event)
void FilamentMapDialog::on_cancle(wxCommandEvent &event) { EndModal(wxID_CANCEL); }
void FilamentMapDialog::update_panel_status(PageType page)
{
EndModal(wxID_CANCEL);
if (page == PageType::ptDefault) {
if (m_default_btn && m_default_map_panel) {
m_default_btn->Select(true);
m_default_map_panel->Show();
}
m_manual_btn->Select(false);
m_manual_map_panel->Hide();
m_auto_btn->Select(false);
m_auto_map_panel->Hide();
}
if (page == PageType::ptManual) {
if (m_default_btn && m_default_map_panel) {
m_default_btn->Select(false);
m_default_map_panel->Hide();
}
m_manual_btn->Select(true);
m_manual_map_panel->Show();
m_auto_btn->Select(false);
m_auto_map_panel->Hide();
}
if (page == PageType::ptAuto) {
if (m_default_btn && m_default_map_panel) {
m_default_btn->Select(false);
m_default_map_panel->Hide();
}
m_manual_btn->Select(false);
m_manual_map_panel->Hide();
m_auto_btn->Select(true);
m_auto_map_panel->Show();
}
Layout();
Fit();
}
void FilamentMapDialog::on_switch_mode(wxCommandEvent &event)
{
bool value = dynamic_cast<SwitchButton *>(event.GetEventObject())->GetValue();
if (value) { // auto
m_manual_left_panel->Hide();
m_manual_right_panel->Hide();
m_switch_filament_btn->Hide();
m_below_tip_text->Hide();
if (m_has_auto_result) {
m_auto_left_panel->Show();
m_auto_right_panel->Show();
m_switch_filament_btn_auto->Show();
m_tip_text->SetLabel(auto_tips_with_result);
}
else {
m_auto_left_panel->Hide();
m_auto_right_panel->Hide();
m_switch_filament_btn_auto->Hide();
m_tip_text->SetLabel(auto_tips);
}
} else { // manual
m_manual_left_panel->Show();
m_manual_right_panel->Show();
m_switch_filament_btn->Show();
m_below_tip_text->Show();
int win_id = event.GetId();
m_page_type = PageType(win_id);
m_auto_left_panel->Hide();
m_auto_right_panel->Hide();
m_switch_filament_btn_auto->Hide();
m_tip_text->SetLabel(manual_tips);
}
Layout();
Fit();
update_panel_status(m_page_type);
event.Skip();
}
void FilamentMapDialog::on_switch_filaments(wxCommandEvent &event)
void FilamentMapDialog::set_modal_btn_labels(const wxString &ok_label, const wxString &cancel_label)
{
std::vector<ColorPanel *> left_blocks = m_manual_left_panel->get_filament_blocks();
std::vector<ColorPanel *> right_blocks = m_manual_right_panel->get_filament_blocks();
for (ColorPanel* block : left_blocks) {
m_manual_right_panel->AddColorBlock(block->GetColor(), block->GetFilamentId(), false);
m_manual_left_panel->RemoveColorBlock(block, false);
}
for (auto block : right_blocks) {
m_manual_left_panel->AddColorBlock(block->GetColor(), block->GetFilamentId(), false);
m_manual_right_panel->RemoveColorBlock(block, false);
}
Layout();
Fit();
m_ok_btn->SetLabel(ok_label);
m_cancel_btn->SetLabel(cancel_label);
}
}} // namespace Slic3r::GUI

View File

@@ -2,10 +2,13 @@
#define slic3r_FilamentMapDialog_hpp_
#include "GUI.hpp"
#include "FilamentMapPanel.hpp"
#include <wx/simplebook.h>
#include <wx/dialog.h>
#include <wx/timer.h>
#include <vector>
#include "SelectMachine.hpp"
#include "CapsuleButton.hpp"
class SwitchButton;
class ScalableButton;
@@ -17,42 +20,52 @@ class DynamicPrintConfig;
namespace GUI {
class DragDropPanel;
class FilamentMapDialog : public wxDialog
{
enum PageType {
ptAuto,
ptManual,
ptDefault
};
public:
FilamentMapDialog(wxWindow *parent,
const DynamicPrintConfig *config,
const std::vector<std::string>& filament_color,
const std::vector<int> &filament_map,
const std::vector<int> &extruders,
bool is_auto,
bool has_auto_result
const std::vector<int> &filaments,
const FilamentMapMode mode,
bool show_default=true
);
bool is_auto() const;
FilamentMapMode get_mode();
const std::vector<int>& get_filament_maps() const { return m_filament_map; }
int ShowModal();
void set_modal_btn_labels(const wxString& left_label, const wxString& right_label);
private:
void on_ok(wxCommandEvent &event);
void on_cancle(wxCommandEvent &event);
void on_switch_mode(wxCommandEvent &event);
void on_switch_filaments(wxCommandEvent &event);
void update_panel_status(PageType page);
private:
FilamentMapManualPanel* m_manual_map_panel;
FilamentMapAutoPanel* m_auto_map_panel;
FilamentMapDefaultPanel* m_default_map_panel;
CapsuleButton* m_auto_btn;
CapsuleButton* m_manual_btn;
CapsuleButton* m_default_btn;
Button* m_ok_btn;
Button* m_cancel_btn;
PageType m_page_type;
private:
wxStaticText * m_tip_text;
wxStaticText * m_below_tip_text;
SwitchButton * m_mode_switch_btn;
wxBoxSizer * m_extruder_panel_sizer;
DragDropPanel* m_manual_left_panel;
DragDropPanel* m_manual_right_panel;
DragDropPanel* m_auto_left_panel;
DragDropPanel* m_auto_right_panel;
ScalableButton* m_switch_filament_btn;
ScalableButton* m_switch_filament_btn_auto; // for placeholder
private:
const DynamicPrintConfig* m_config;
std::vector<int> m_filament_map;
bool m_has_auto_result;
std::vector<std::string> m_filament_color;
};
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,324 @@
#include "FilamentMapPanel.hpp"
#include "GUI_App.hpp"
#include "wx/graphics.h"
namespace Slic3r { namespace GUI {
FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *parent,
const std::vector<std::string> &color,
const std::vector<int> &filament_list,
const std::vector<int> &filament_map)
: wxPanel(parent), m_filament_map(filament_map), m_filament_color(color), m_filament_list(filament_list)
{
SetBackgroundColour(wxColor(255, 255, 255));
auto top_sizer = new wxBoxSizer(wxVERTICAL);
m_description = new Label(this, _L("We will slice according to this grouping method:"));
top_sizer->Add(m_description, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
top_sizer->AddSpacer(FromDIP(8));
auto drag_sizer = new wxBoxSizer(wxHORIZONTAL);
m_left_panel = new DragDropPanel(this, wxT("Left Nozzle:"), false);
m_right_panel = new DragDropPanel(this, wxT("Right Nozzle:"), false);
m_switch_btn = new ScalableButton(this, wxID_ANY, "switch_filament_maps");
for (size_t idx = 0; idx < m_filament_map.size(); ++idx) {
auto iter = std::find(m_filament_list.begin(), m_filament_list.end(), idx + 1);
if (iter == m_filament_list.end()) continue;
wxColor color = Hex2Color(m_filament_color[idx]);
if (m_filament_map[idx] == 1) {
m_left_panel->AddColorBlock(color, idx + 1);
} else {
assert(m_filament_map[idx] == 2);
m_right_panel->AddColorBlock(color, idx + 1);
}
}
drag_sizer->Add(m_left_panel, 1, wxALIGN_CENTER | wxEXPAND | wxLEFT, FromDIP(20));
drag_sizer->AddSpacer(FromDIP(7));
drag_sizer->Add(m_switch_btn, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(1));
drag_sizer->AddSpacer(FromDIP(7));
drag_sizer->Add(m_right_panel, 1, wxALIGN_CENTER | wxEXPAND | wxRIGHT, FromDIP(20));
top_sizer->Add(drag_sizer, 0, wxALIGN_CENTER|wxEXPAND);
m_tips = new Label(this, _L("Tips: You can drag the filaments to change which extruder they are assigned to.\n"
"But your filament arrangement may not be the most filament-efficient."));
m_tips->SetFont(Label::Body_14);
m_tips->SetForegroundColour(wxColour("#6B6B6B"));
top_sizer->AddSpacer(FromDIP(8));
top_sizer->Add(m_tips, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
m_switch_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSwitchFilament, this);
SetSizer(top_sizer);
Layout();
Fit();
GUI::wxGetApp().UpdateDarkUIWin(this);
}
void FilamentMapManualPanel::OnSwitchFilament(wxCommandEvent &)
{
auto left_blocks = m_left_panel->get_filament_blocks();
auto right_blocks = m_right_panel->get_filament_blocks();
for (auto &block : left_blocks) {
m_right_panel->AddColorBlock(block->GetColor(), block->GetFilamentId(), false);
m_left_panel->RemoveColorBlock(block, false);
}
for (auto &block : right_blocks) {
m_left_panel->AddColorBlock(block->GetColor(), block->GetFilamentId(), false);
m_right_panel->RemoveColorBlock(block, false);
}
Layout();
Fit();
}
void FilamentMapManualPanel::Hide()
{
m_left_panel->Hide();
m_right_panel->Hide();
m_switch_btn->Hide();
wxPanel::Hide();
}
void FilamentMapManualPanel::Show()
{
m_left_panel->Show();
m_right_panel->Show();
m_switch_btn->Show();
wxPanel::Show();
}
GUI::FilamentMapBtnPanel::FilamentMapBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon) : wxPanel(parent)
{
SetBackgroundColour(*wxWHITE);
SetBackgroundStyle(wxBG_STYLE_PAINT);
m_hover = false;
const int horizontal_margin = FromDIP(12);
auto sizer = new wxBoxSizer(wxVERTICAL);
auto bmp = create_scaled_bitmap(icon, nullptr, 20);
m_btn = new wxBitmapButton(this, wxID_ANY, bmp, wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
m_btn->SetBackgroundColour(*wxWHITE);
m_label = new wxStaticText(this, wxID_ANY, label);
m_label->SetFont(Label::Head_14);
auto label_sizer = new wxBoxSizer(wxHORIZONTAL);
label_sizer->AddStretchSpacer();
label_sizer->Add(m_btn, 0, wxALIGN_CENTER|wxEXPAND);
label_sizer->Add(m_label, 0, wxALIGN_CENTER|wxEXPAND);
label_sizer->AddStretchSpacer();
sizer->AddSpacer(FromDIP(32));
sizer->Add(label_sizer, 0, wxALIGN_CENTER | wxEXPAND);
sizer->AddSpacer(FromDIP(24));
auto detail_sizer = new wxBoxSizer(wxHORIZONTAL);
m_detail = new Label(this, detail);
m_detail->SetFont(Label::Body_12);
m_detail->SetForegroundColour(wxColour("#6B6B6B"));
m_detail->Wrap(FromDIP(180));
detail_sizer->AddStretchSpacer();
detail_sizer->Add(m_detail, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, horizontal_margin);
detail_sizer->AddStretchSpacer();
sizer->Add(detail_sizer, 0, wxALIGN_CENTER | wxEXPAND);
sizer->AddSpacer(FromDIP(10));
SetSizer(sizer);
Layout();
Fit();
GUI::wxGetApp().UpdateDarkUIWin(this);
auto forward_click_to_parent = [this](wxMouseEvent &event) {
wxCommandEvent click_event(wxEVT_LEFT_DOWN, GetId());
click_event.SetEventObject(this);
this->ProcessEvent(click_event);
};
m_btn->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
m_label->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
m_detail->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent);
Bind(wxEVT_PAINT, &FilamentMapBtnPanel::OnPaint, this);
Bind(wxEVT_ENTER_WINDOW, &FilamentMapBtnPanel::OnEnterWindow, this);
Bind(wxEVT_LEAVE_WINDOW, &FilamentMapBtnPanel::OnLeaveWindow, this);
}
void FilamentMapBtnPanel::OnPaint(wxPaintEvent &event)
{
wxAutoBufferedPaintDC dc(this);
wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
if (gc) {
dc.Clear();
wxRect rect = GetClientRect();
gc->SetBrush(wxTransparentColour);
gc->DrawRoundedRectangle(0, 0, rect.width, rect.height, 0);
wxColour bg_color = m_selected ? wxColour("#EBF9F0") : wxColour("#FFFFFF");
wxColour border_color = m_hover || m_selected ? wxColour("#00AE42") : wxColour("#CECECE");
int cornerRadius = 8;
gc->SetBrush(wxBrush(bg_color));
gc->SetPen(wxPen(border_color, 2));
gc->DrawRoundedRectangle(1, 1, rect.width - 2, rect.height - 2, cornerRadius);
delete gc;
}
}
void FilamentMapBtnPanel::UpdateStatus()
{
const wxColour selected_color = wxColour("#EBF9F0");
const wxColour normal_color = wxColour("#FFFFFF");
if (m_selected) {
m_btn->SetBackgroundColour(selected_color);
m_label->SetBackgroundColour(selected_color);
m_detail->SetBackgroundColour(selected_color);
} else {
m_btn->SetBackgroundColour(normal_color);
m_label->SetBackgroundColour(normal_color);
m_detail->SetBackgroundColour(normal_color);
}
}
void FilamentMapBtnPanel::OnEnterWindow(wxMouseEvent &event)
{
if (!m_hover) {
m_hover = true;
UpdateStatus();
Refresh();
event.Skip();
}
}
void FilamentMapBtnPanel::OnLeaveWindow(wxMouseEvent &event)
{
if (m_hover) {
wxPoint pos = this->ScreenToClient(wxGetMousePosition());
if (this->GetClientRect().Contains(pos)) return;
m_hover = false;
UpdateStatus();
Refresh();
event.Skip();
}
}
void FilamentMapBtnPanel::Select(bool selected)
{
m_selected = selected;
UpdateStatus();
Refresh();
}
void GUI::FilamentMapBtnPanel::Hide()
{
m_btn->Hide();
m_label->Hide();
m_detail->Hide();
wxPanel::Hide();
}
void GUI::FilamentMapBtnPanel::Show()
{
m_btn->Show();
m_label->Show();
m_detail->Show();
wxPanel::Show();
}
FilamentMapAutoPanel::FilamentMapAutoPanel(wxWindow *parent, FilamentMapMode mode) : wxPanel(parent)
{
static const wxString AutoForFlushDetail = _L("Disregrad the filaments in AMS. Optimize filament usage "
"by calculating the best allocation for the left and right "
"nozzles. Arrange the filaments according on the printer according to "
"the slicing results.");
static const wxString AutoForMatchDetail = _L("Based on the current filaments in the AMS, allocate the "
"filaments to the left and right nozzles.");
auto sizer = new wxBoxSizer(wxHORIZONTAL);
m_flush_panel = new FilamentMapBtnPanel(this, _L("Material-Saving Mode"), AutoForFlushDetail, "flush_mode_panel_icon");
m_match_panel = new FilamentMapBtnPanel(this, _L("Convenient Mode"), AutoForMatchDetail,"match_mode_panel_icon");
sizer->AddStretchSpacer();
sizer->Add(m_flush_panel, 1, wxEXPAND);
sizer->AddSpacer(FromDIP(12));
sizer->Add(m_match_panel, 1, wxEXPAND);
sizer->AddStretchSpacer();
m_flush_panel->Bind(wxEVT_LEFT_DOWN, [this](auto &event) { this->OnModeSwitch(FilamentMapMode::fmmAutoForFlush); });
m_match_panel->Bind(wxEVT_LEFT_DOWN, [this](auto &event) { this->OnModeSwitch(FilamentMapMode::fmmAutoForMatch); });
m_mode = mode;
UpdateStatus();
SetSizerAndFit(sizer);
Layout();
GUI::wxGetApp().UpdateDarkUIWin(this);
}
void FilamentMapAutoPanel::Hide()
{
m_flush_panel->Hide();
m_match_panel->Hide();
wxPanel::Hide();
}
void FilamentMapAutoPanel::Show()
{
m_flush_panel->Show();
m_match_panel->Show();
wxPanel::Show();
}
void FilamentMapAutoPanel::UpdateStatus()
{
if (m_mode == fmmAutoForFlush) {
m_flush_panel->Select(true);
m_match_panel->Select(false);
} else {
m_flush_panel->Select(false);
m_match_panel->Select(true);
}
}
void FilamentMapAutoPanel::OnModeSwitch(FilamentMapMode mode)
{
m_mode = mode;
UpdateStatus();
}
FilamentMapDefaultPanel::FilamentMapDefaultPanel(wxWindow *parent) : wxPanel(parent)
{
auto sizer = new wxBoxSizer(wxHORIZONTAL);
m_label = new Label(this, _L("The material allocation for the current disk follows the global settings."));
m_label->SetFont(Label::Body_14);
m_label->SetBackgroundColour(*wxWHITE);
sizer->AddStretchSpacer();
sizer->Add(m_label, 1, wxEXPAND|wxALIGN_CENTER);
sizer->AddStretchSpacer();
SetSizerAndFit(sizer);
Layout();
GUI::wxGetApp().UpdateDarkUIWin(this);
}
void FilamentMapDefaultPanel::Hide()
{
m_label->Hide();
wxPanel::Hide();
}
void FilamentMapDefaultPanel::Show()
{
m_label->Show();
wxPanel::Show();
}
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,89 @@
#ifndef FILAMENT_MAP_PANEL_HPP
#define FILAMENT_MAP_PANEL_HPP
#include "GUI.hpp"
#include "DragDropPanel.hpp"
#include "Widgets/SwitchButton.hpp"
namespace Slic3r { namespace GUI {
class FilamentMapManualPanel : public wxPanel
{
public:
FilamentMapManualPanel(wxWindow *parent, const std::vector<std::string> &color, const std::vector<int> &filament_list, const std::vector<int> &filament_map);
std::vector<int> GetFilamentMaps() const { return m_filament_map; }
std::vector<int> GetLeftFilaments() const { return m_left_panel->GetAllFilaments(); }
std::vector<int> GetRightFilaments() const { return m_right_panel->GetAllFilaments(); }
void Hide();
void Show();
private:
void OnSwitchFilament(wxCommandEvent &);
DragDropPanel *m_left_panel;
DragDropPanel *m_right_panel;
Label *m_description;
Label *m_tips;
ScalableButton *m_switch_btn;
std::vector<int> m_filament_map;
std::vector<int> m_filament_list;
std::vector<std::string> m_filament_color;
};
class FilamentMapBtnPanel : public wxPanel
{
public:
FilamentMapBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon_path);
void Hide();
void Show();
void Select(bool selected);
protected:
void OnPaint(wxPaintEvent &event);
private:
void OnEnterWindow(wxMouseEvent &event);
void OnLeaveWindow(wxMouseEvent &evnet);
void UpdateStatus();
wxBitmapButton *m_btn;
wxStaticText *m_label;
Label *m_detail;
bool m_hover{false};
bool m_selected{false};
};
class FilamentMapAutoPanel : public wxPanel
{
public:
FilamentMapAutoPanel(wxWindow *parent, FilamentMapMode mode);
void Hide();
void Show();
FilamentMapMode GetMode() const { return m_mode; }
private:
void OnModeSwitch(FilamentMapMode mode);
void UpdateStatus();
FilamentMapBtnPanel *m_flush_panel;
FilamentMapBtnPanel *m_match_panel;
FilamentMapMode m_mode;
};
class FilamentMapDefaultPanel : public wxPanel
{
public:
FilamentMapDefaultPanel(wxWindow *parent);
void Hide();
void Show();
private:
Label *m_label;
};
}} // namespace Slic3r::GUI
#endif

View File

@@ -4410,7 +4410,7 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
Plater *plater = wxGetApp().plater();
wxCommandEvent evt(EVT_OPEN_FILAMENT_MAP_SETTINGS_DIALOG);
evt.SetEventObject(plater);
evt.SetInt(0b0010); //0010 means from gcode view, manual mode
evt.SetInt(1); // 1 means from gcode viewer
wxPostEvent(plater, evt);
}
}

View File

@@ -62,6 +62,7 @@
#include "ConfigWizard.hpp"
#include "Widgets/WebView.hpp"
#include "DailyTips.hpp"
#include "FilamentMapDialog.hpp"
#ifdef _WIN32
#include <dbt.h>
@@ -1578,20 +1579,7 @@ wxBoxSizer* MainFrame::create_side_tools()
sizer->Layout();
// m_publish_btn->Bind(wxEVT_BUTTON, [this](auto& e) {
// CallAfter([this] {
// wxGetApp().open_publish_page_dialog();
// if (!wxGetApp().getAgent()) {
// BOOST_LOG_TRIVIAL(info) << "publish: no agent";
// return;
// }
// // record
// json j;
// NetworkAgent* agent = GUI::wxGetApp().getAgent();
// });
// });
m_filament_group_popup = new FilamentGroupPopup(m_slice_btn);
m_slice_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
@@ -1602,14 +1590,62 @@ wxBoxSizer* MainFrame::create_side_tools()
//this->m_plater->select_view_3D("Preview");
m_plater->exit_gizmo();
m_plater->update(true, true);
if (m_slice_select == eSliceAll)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_ALL));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_PLATE));
this->m_tabpanel->SetSelection(tpPreview);
bool slice = true;
auto full_config = wxGetApp().preset_bundle->full_config();
std::vector<int>g_filament_map = full_config.option<ConfigOptionInts>("filament_map")->values;
FilamentMapMode g_filament_map_mode = get_prefered_map_mode();
if (is_pop_up_required()) {
auto filament_colors = full_config.option<ConfigOptionStrings>("filament_colour")->values;
g_filament_map.resize(filament_colors.size());
std::vector<int> filament_lists(filament_colors.size());
std::iota(filament_lists.begin(), filament_lists.end(), 1);
FilamentMapDialog filament_dlg(this,
filament_colors,
g_filament_map,
filament_lists,
FilamentMapMode::fmmManual,
false
);
auto ret = filament_dlg.ShowModal();
if (ret == wxID_OK) {
g_filament_map_mode = filament_dlg.get_mode();
g_filament_map = filament_dlg.get_filament_maps();
}
else {
slice = false;
}
}
if (slice) {
m_plater->set_global_filament_map_mode(g_filament_map_mode);
if (g_filament_map_mode == FilamentMapMode::fmmManual)
m_plater->set_global_filament_map(g_filament_map);
if (m_slice_select == eSliceAll)
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_ALL));
else
wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SLICE_PLATE));
this->m_tabpanel->SetSelection(tpPreview);
}
});
m_slice_btn->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) {
m_filament_group_popup->SetSize(wxSize(FromDIP(380),-1));
wxPoint pos = m_slice_btn->ClientToScreen(wxPoint(0, 0));
pos.y += m_slice_btn->GetRect().height * 1.25;
pos.x -= (m_slice_option_btn->GetRect().width + m_filament_group_popup->GetRect().width * 0.6);
m_filament_group_popup->SetPosition(pos);
m_filament_group_popup->tryPopup(m_plater->check_ams_status());
});
m_slice_btn->Bind(wxEVT_LEAVE_WINDOW, [this](auto& event) {
m_filament_group_popup->tryClose();
});
m_print_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event)
{
//this->m_plater->select_view_3D("Preview");
@@ -3870,6 +3906,7 @@ void MainFrame::technology_changed()
m_menubar->SetMenuLabel(id, pt == ptSLA ? _omitL("Material Settings") : _L("Filament Settings"));
}
//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.

View File

@@ -25,6 +25,8 @@
#include "UnsavedChangesDialog.hpp"
#include "Widgets/SideButton.hpp"
#include "Widgets/SideMenuPopup.hpp"
#include "FilamentGroupPopup.hpp"
#include <boost/property_tree/ptree_fwd.hpp>
@@ -338,6 +340,7 @@ public:
void technology_changed();
//BBS
void load_url(wxString url);
void load_printer_url(wxString url, wxString apikey = "");
@@ -393,6 +396,8 @@ public:
SideButton* m_slice_option_btn{ nullptr };
SideButton* m_print_btn{ nullptr };
SideButton* m_print_option_btn{ nullptr };
FilamentGroupPopup* m_filament_group_popup{ nullptr };
mutable bool m_slice_enable{ true };
mutable bool m_print_enable{ true };
bool get_enable_slice_status();

View File

@@ -15322,9 +15322,9 @@ void Plater::open_platesettings_dialog(wxCommandEvent& evt) {
void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt)
{
PartPlate* curr_plate = p->partplate_list.get_curr_plate();
int value = evt.GetInt();
bool is_auto = value & 1; //0000 means manual, 0001 means auto
bool need_slice = value & (1 << 1); //0010 means from gcode view, 0000 means not from gcode view
int value = evt.GetInt(); //1 means from gcode view
bool force_manual = value == 1; // If from gcode view, should display manual page
bool need_slice = value ==1; // If from gcode view, should slice
const auto& project_config = wxGetApp().preset_bundle->project_config;
auto filament_colors = config()->option<ConfigOptionStrings>("filament_colour")->values;
@@ -15333,20 +15333,22 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt)
if (plate_filament_maps.size() != filament_colors.size()) // refine it later, save filament map to app config
plate_filament_maps.resize(filament_colors.size(), 1);
FilamentMapMode display_mode = force_manual ? FilamentMapMode::fmmManual : plate_filament_map_mode;
FilamentMapDialog filament_dlg(this,
config(),
filament_colors,
plate_filament_maps,
curr_plate->get_extruders(true),
plate_filament_map_mode < FilamentMapMode::fmmManual,
false
display_mode,
true
);
if (filament_dlg.ShowModal() == wxID_OK) {
std::vector<int> new_filament_maps = filament_dlg.get_filament_maps();
std::vector<int> old_filament_maps = plate_filament_maps;
std::vector<int> old_filament_maps = curr_plate->get_filament_maps();
FilamentMapMode old_map_mode = plate_filament_map_mode;
FilamentMapMode new_map_mode = filament_dlg.is_auto() ? fmmAutoForFlush : fmmManual;
FilamentMapMode new_map_mode = filament_dlg.get_mode();
bool need_invalidate = (old_map_mode != new_map_mode ||
old_filament_maps != new_filament_maps);
@@ -15538,7 +15540,7 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModi
if (!ret) {
PartPlate * curr_plate = p->partplate_list.get_curr_plate();
wxCommandEvent evt(EVT_OPEN_FILAMENT_MAP_SETTINGS_DIALOG);
evt.SetInt(curr_plate->get_filament_map_mode() < FilamentMapMode::fmmManual ? 1 : 0);
evt.SetInt(0); // 0 means not from gcodeviewer
evt.SetEventObject(this);
wxPostEvent(this, evt);
} else {

View File

@@ -494,6 +494,7 @@ public:
void on_filaments_delete(size_t extruders_count, size_t filament_id, int replace_filament_id = -1);
// BBS
void on_bed_type_change(BedType bed_type);
bool update_filament_colors_in_full_config();
void config_change_notification(const DynamicPrintConfig &config, const std::string& key);
void on_config_change(const DynamicPrintConfig &config);

View File

@@ -1275,6 +1275,10 @@ wxWindow* PreferencesDialog::create_general_page()
auto item_darkmode = create_item_darkmode_checkbox(_L("Enable Dark mode"), page,_L("Enable Dark mode"), 50, "dark_color_mode");
#endif
auto title_filament_group = create_item_title(_L("Filament Group"), page, _L("Filament Group"));
auto item_ignore_ext_filament = create_item_checkbox(_L("Ignore ext filament when auto grouping."), page, _L("Ignore ext filament when auto grouping"), 50, "ignore_ext_filament_when_group");
auto item_pop_filament_group_mode = create_item_checkbox(_L("Pop up to select filament map mode."), page, _L("Pop up to select filament map mode"), 50, "pop_up_filament_map_mode");
auto title_develop_mode = create_item_title(_L("Develop mode"), page, _L("Develop mode"));
auto item_develop_mode = create_item_checkbox(_L("Develop mode"), page, _L("Develop mode"), 50, "developer_mode");
auto item_skip_ams_blacklist_check = create_item_checkbox(_L("Skip AMS blacklist check"), page, _L("Skip AMS blacklist check"), 50, "skip_ams_blacklist_check");
@@ -1347,6 +1351,10 @@ wxWindow* PreferencesDialog::create_general_page()
sizer_page->Add(item_darkmode, 0, wxEXPAND, FromDIP(3));
#endif
sizer_page->Add(title_filament_group, 0, wxTOP | wxEXPAND, FromDIP(20));
sizer_page->Add(item_ignore_ext_filament, 0, wxEXPAND, FromDIP(3));
sizer_page->Add(item_pop_filament_group_mode, 0, wxEXPAND, FromDIP(3));
sizer_page->Add(title_develop_mode, 0, wxTOP | wxEXPAND, FromDIP(20));
sizer_page->Add(item_develop_mode, 0, wxTOP, FromDIP(3));
sizer_page->Add(item_skip_ams_blacklist_check, 0, wxTOP, FromDIP(3));