diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index be7c492a3e..fea794c943 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -114,6 +114,10 @@ set(SLIC3R_GUI_SOURCES GUI/IconManager.hpp GUI/MainFrame.cpp GUI/MainFrame.hpp + GUI/FrequentlyChangedParameters.cpp + GUI/FrequentlyChangedParameters.hpp + GUI/Sidebar.cpp + GUI/Sidebar.hpp GUI/Plater.cpp GUI/Plater.hpp GUI/PresetComboBoxes.hpp diff --git a/src/slic3r/GUI/FrequentlyChangedParameters.cpp b/src/slic3r/GUI/FrequentlyChangedParameters.cpp new file mode 100644 index 0000000000..8717ce1311 --- /dev/null +++ b/src/slic3r/GUI/FrequentlyChangedParameters.cpp @@ -0,0 +1,331 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2022 Michael Kirsch +///|/ Copyright (c) 2021 Boleslaw Ciesielski +///|/ Copyright (c) 2019 John Drake @foxox +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#include "FrequentlyChangedParameters.hpp" +#include "Plater.hpp" + +#include + +#include +#include + +#include "libslic3r/PresetBundle.hpp" + +#include "GUI_App.hpp" +#include "wxExtensions.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "I18N.hpp" + +#include "WipeTowerDialog.hpp" + +using Slic3r::Preset; +using Slic3r::GUI::format_wxstr; + +namespace Slic3r { +namespace GUI { + +// Trigger Plater::schedule_background_process(). +wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); + +FreqChangedParams::FreqChangedParams(wxWindow* parent) +{ + DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; + + // Frequently changed parameters for FFF_technology + + m_og_fff = std::make_shared(parent, ""); + m_og_fff->set_config(config); + m_og_fff->hide_labels(); + + m_og_fff->on_change = [config, this](t_config_option_key opt_key, boost::any value) { + Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); + if (!tab_print) return; + + if (opt_key == "fill_density") { + tab_print->update_dirty(); + tab_print->reload_config(); + tab_print->update(); + } + else + { + DynamicPrintConfig new_conf = *config; + if (opt_key == "brim") { + double new_val; + double brim_width = config->opt_float("brim_width"); + if (boost::any_cast(value) == true) + { + new_val = m_brim_width == 0.0 ? 5 : + m_brim_width < 0.0 ? m_brim_width * (-1) : + m_brim_width; + } + else { + m_brim_width = brim_width * (-1); + new_val = 0; + } + new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); + } + else { + assert(opt_key == "support"); + const wxString& selection = boost::any_cast(value); + PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + + auto support_material = selection == _("None") ? false : true; + new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); + + if (selection == _("Everywhere")) { + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); + if (printer_technology == ptFFF) + new_conf.set_key_value("support_material_auto", new ConfigOptionBool(true)); + } else if (selection == _("Support on build plate only")) { + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); + if (printer_technology == ptFFF) + new_conf.set_key_value("support_material_auto", new ConfigOptionBool(true)); + } else if (selection == _("For support enforcers only")) { + assert(printer_technology == ptFFF); + new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); + new_conf.set_key_value("support_material_auto", new ConfigOptionBool(false)); + } + } + tab_print->load_config(new_conf); + } + }; + + + Line line = Line { "", "" }; + + ConfigOptionDef support_def; + support_def.label = L("Supports"); + support_def.type = coStrings; + support_def.tooltip = L("Select what kind of support do you need"); + support_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { + L("None"), + L("Support on build plate only"), + L("For support enforcers only"), + L("Everywhere") + }); + support_def.set_default_value(new ConfigOptionStrings{ "None" }); + Option option = Option(support_def, "support"); + option.opt.full_width = true; + line.append_option(option); + + /* Not a best solution, but + * Temporary workaround for right border alignment + */ + auto empty_widget = [this] (wxWindow* parent) { + auto sizer = new wxBoxSizer(wxHORIZONTAL); + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, + wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); + sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); + m_empty_buttons.push_back(btn); + return sizer; + }; + line.append_widget(empty_widget); + + m_og_fff->append_line(line); + + + line = Line { "", "" }; + + option = m_og_fff->get_option("fill_density"); + option.opt.label = L("Infill"); + option.opt.width = 8; + option.opt.sidetext = " "; + line.append_option(option); + + m_brim_width = config->opt_float("brim_width"); + ConfigOptionDef def; + def.label = L("Brim"); + def.type = coBool; + def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); + def.gui_type = ConfigOptionDef::GUIType::undefined; + def.set_default_value(new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }); + option = Option(def, "brim"); + option.opt.sidetext = ""; + line.append_option(option); + + auto wiping_dialog_btn = [this](wxWindow* parent) { + m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _L("Purging volumes") + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); + wxGetApp().SetWindowVariantForButton(m_wiping_dialog_button); + wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL); + m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) + { + auto &project_config = wxGetApp().preset_bundle->project_config; + const std::vector &init_matrix = (project_config.option("wiping_volumes_matrix"))->values; + const std::vector &init_extruders = (project_config.option("wiping_volumes_extruders"))->values; + + const std::vector extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); + + WipingDialog dlg(parent, cast(init_matrix), cast(init_extruders), extruder_colours); + + if (dlg.ShowModal() == wxID_OK) { + std::vector matrix = dlg.get_matrix(); + std::vector extruders = dlg.get_extruders(); + (project_config.option("wiping_volumes_matrix"))->values = std::vector(matrix.begin(), matrix.end()); + (project_config.option("wiping_volumes_extruders"))->values = std::vector(extruders.begin(), extruders.end()); + // Update Project dirty state, update application title bar. + wxGetApp().plater()->update_project_dirty_from_presets(); + wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); + } + })); + + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, + wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); + sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, + int(0.3 * wxGetApp().em_unit())); + m_empty_buttons.push_back(btn); + + return sizer; + }; + line.append_widget(wiping_dialog_btn); + m_og_fff->append_line(line); + + m_og_fff->activate(); + + Choice* choice = dynamic_cast(m_og_fff->get_field("support")); + choice->suppress_scroll(); + + // Frequently changed parameters for SLA_technology + + m_og_sla = std::make_shared(parent, ""); + m_og_sla->hide_labels(); + DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + m_og_sla->set_config(config_sla); + + m_og_sla->on_change = [config_sla](t_config_option_key opt_key, boost::any value) { + Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT); + if (!tab) return; + + DynamicPrintConfig new_conf = *config_sla; + if (opt_key == "pad") { + const wxString& selection = boost::any_cast(value); + + const bool pad_enable = selection == _("None") ? false : true; + new_conf.set_key_value("pad_enable", new ConfigOptionBool(pad_enable)); + + if (selection == _("Below object")) + new_conf.set_key_value("pad_around_object", new ConfigOptionBool(false)); + else if (selection == _("Around object")) + new_conf.set_key_value("pad_around_object", new ConfigOptionBool(true)); + } + else + { + assert(opt_key == "support"); + const wxString& selection = boost::any_cast(value); + + const bool supports_enable = selection == _("None") ? false : true; + new_conf.set_key_value("supports_enable", new ConfigOptionBool(supports_enable)); + + std::string treetype = get_sla_suptree_prefix(new_conf); + + if (selection == _("Everywhere")) { + new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(false)); + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); + } + else if (selection == _("Support on build plate only")) { + new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(true)); + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); + } + else if (selection == _("For support enforcers only")) { + new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(true)); + } + } + + tab->load_config(new_conf); + tab->update_dirty(); + }; + + line = Line{ "", "" }; + + ConfigOptionDef support_def_sla = support_def; + support_def_sla.set_default_value(new ConfigOptionStrings{ "None" }); + option = Option(support_def_sla, "support"); + option.opt.full_width = true; + line.append_option(option); + line.append_widget(empty_widget); + m_og_sla->append_line(line); + + line = Line{ "", "" }; + + ConfigOptionDef pad_def; + pad_def.label = L("Pad"); + pad_def.type = coStrings; + pad_def.tooltip = L("Select what kind of pad do you need"); + pad_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { + L("None"), + L("Below object"), + L("Around object") + }); + pad_def.set_default_value(new ConfigOptionStrings{ "Below object" }); + option = Option(pad_def, "pad"); + option.opt.full_width = true; + line.append_option(option); + line.append_widget(empty_widget); + + m_og_sla->append_line(line); + + m_og_sla->activate(); + choice = dynamic_cast(m_og_sla->get_field("support")); + choice->suppress_scroll(); + choice = dynamic_cast(m_og_sla->get_field("pad")); + choice->suppress_scroll(); + + m_sizer = new wxBoxSizer(wxVERTICAL); + m_sizer->Add(m_og_fff->sizer, 0, wxEXPAND); + m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND); +} + +void FreqChangedParams::msw_rescale() +{ + m_og_fff->msw_rescale(); + m_og_sla->msw_rescale(); +} + +void FreqChangedParams::sys_color_changed() +{ + m_og_fff->sys_color_changed(); + m_og_sla->sys_color_changed(); + + for (auto btn: m_empty_buttons) + btn->sys_color_changed(); + + wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); +} + +void FreqChangedParams::Show(bool is_fff) const +{ + const bool is_wdb_shown = m_wiping_dialog_button->IsShown(); + m_og_fff->Show(is_fff); + m_og_sla->Show(!is_fff); + + // correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden + if (is_fff && !is_wdb_shown) + m_wiping_dialog_button->Hide(); +} + +ConfigOptionsGroup* FreqChangedParams::get_og(bool is_fff) +{ + return is_fff ? m_og_fff.get() : m_og_sla.get(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/FrequentlyChangedParameters.hpp b/src/slic3r/GUI/FrequentlyChangedParameters.hpp new file mode 100644 index 0000000000..5bc984241c --- /dev/null +++ b/src/slic3r/GUI/FrequentlyChangedParameters.hpp @@ -0,0 +1,64 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_FreqChangedParams_hpp_ +#define slic3r_FreqChangedParams_hpp_ + +#include "Event.hpp" + +class wxButton; +class wxSizer; +class wxWindow; +class ScalableButton; + +namespace Slic3r { + +namespace GUI { + +wxDECLARE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); + +class ConfigOptionsGroup; + +class FreqChangedParams +{ + double m_brim_width = 0.0; + wxButton* m_wiping_dialog_button{ nullptr }; + wxSizer* m_sizer {nullptr}; + + std::shared_ptr m_og_fff; + std::shared_ptr m_og_sla; + + std::vector m_empty_buttons; + +public: + + FreqChangedParams(wxWindow* parent); + ~FreqChangedParams() = default; + + wxButton* get_wiping_dialog_button() noexcept { return m_wiping_dialog_button; } + wxSizer* get_sizer() noexcept { return m_sizer; } + void Show(bool is_fff) const; + + ConfigOptionsGroup* get_og(bool is_fff); + + void msw_rescale(); + void sys_color_changed(); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 26b3530d52..888f87b613 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -50,11 +50,6 @@ #include #include #include -#ifdef _WIN32 -#include -#include -#include -#endif #include @@ -65,16 +60,13 @@ #include "libslic3r/Format/OBJ.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/SLA/Hollowing.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/SLA/ReprojectPointsOnMesh.hpp" -#include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/PresetBundle.hpp" -#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/miniz_extension.hpp" // For stl export @@ -85,9 +77,7 @@ #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_ObjectManipulation.hpp" -#include "GUI_ObjectLayers.hpp" #include "GUI_Utils.hpp" -#include "GUI_Geometry.hpp" #include "GUI_Factories.hpp" #include "wxExtensions.hpp" #include "MainFrame.hpp" @@ -101,10 +91,8 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" -//#include "Jobs/ArrangeJob.hpp" #include "Jobs/ArrangeJob2.hpp" -//#include "Jobs/FillBedJob.hpp" #include "Jobs/RotoptimizeJob.hpp" #include "Jobs/SLAImportJob.hpp" #include "Jobs/SLAImportDialog.hpp" @@ -113,10 +101,8 @@ #include "Jobs/BoostThreadWorker.hpp" #include "BackgroundSlicingProcess.hpp" #include "PrintHostDialogs.hpp" -#include "ConfigWizard.hpp" #include "../Utils/ASCIIFolding.hpp" #include "../Utils/PrintHost.hpp" -#include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" #include "../Utils/PresetUpdater.hpp" #include "../Utils/Process.hpp" @@ -155,8 +141,6 @@ static const std::pair THUMBNAIL_SIZE_3MF = { 256, 2 namespace Slic3r { namespace GUI { -// Trigger Plater::schedule_background_process(). -wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); // BackgroundSlicingProcess updates UI with slicing progress: Status bar / progress bar has to be updated, possibly scene has to be refreshed, // see PrintBase::SlicingStatus for the content of the message. wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); @@ -189,1395 +173,6 @@ void Plater::show_illegal_characters_warning(wxWindow* parent) _L("the following characters are not allowed:") + " <>:/\\|?*\""); } -// Sidebar widgets - -// struct InfoBox : public wxStaticBox -// { -// InfoBox(wxWindow *parent, const wxString &label) : -// wxStaticBox(parent, wxID_ANY, label) -// { -// SetFont(GUI::small_font().Bold()); -// } -// }; - -class ObjectInfo : public wxStaticBoxSizer -{ - std::string m_warning_icon_name{ "exclamation" }; -public: - ObjectInfo(wxWindow *parent); - - wxStaticBitmap *manifold_warning_icon; - wxStaticBitmap *info_icon; - wxStaticText *info_size; - wxStaticText *info_volume; - wxStaticText *info_facets; - wxStaticText *info_manifold; - - wxStaticText *label_volume; - std::vector sla_hidden_items; - - bool showing_manifold_warning_icon; - void show_sizer(bool show); - void update_warning_icon(const std::string& warning_icon_name); -}; - -ObjectInfo::ObjectInfo(wxWindow *parent) : - wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Info")), wxVERTICAL) -{ - GetStaticBox()->SetFont(wxGetApp().bold_font()); - wxGetApp().UpdateDarkUI(GetStaticBox()); - - auto *grid_sizer = new wxFlexGridSizer(4, 5, 15); - grid_sizer->SetFlexibleDirection(wxHORIZONTAL); - - auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label, wxSizer* sizer_with_icon=nullptr) { - auto *text = new wxStaticText(parent, wxID_ANY, text_label + ":"); - text->SetFont(wxGetApp().small_font()); - *info_label = new wxStaticText(parent, wxID_ANY, ""); - (*info_label)->SetFont(wxGetApp().small_font()); - grid_sizer->Add(text, 0); - if (sizer_with_icon) { - sizer_with_icon->Insert(0, *info_label, 0); - grid_sizer->Add(sizer_with_icon, 0, wxEXPAND); - } - else - grid_sizer->Add(*info_label, 0); - return text; - }; - - init_info_label(&info_size, _L("Size")); - - info_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle("info")); - info_icon->SetToolTip(_L("For a multipart object, this value isn't accurate.\n" - "It doesn't take account of intersections and negative volumes.")); - auto* volume_info_sizer = new wxBoxSizer(wxHORIZONTAL); - volume_info_sizer->Add(info_icon, 0, wxLEFT, 10); - label_volume = init_info_label(&info_volume, _L("Volume"), volume_info_sizer); - - init_info_label(&info_facets, _L("Facets")); -// label_materials = init_info_label(&info_materials, _L("Materials")); - Add(grid_sizer, 0, wxEXPAND); - - info_manifold = new wxStaticText(parent, wxID_ANY, ""); - info_manifold->SetFont(wxGetApp().small_font()); - manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle(m_warning_icon_name)); - auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); - sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); - sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); - Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); - - sla_hidden_items = { label_volume, info_volume, /*label_materials, info_materials*/ }; - - // Fixes layout issues on plater, short BitmapComboBoxes with some Windows scaling, see GH issue #7414. - this->Show(false); -} - -void ObjectInfo::show_sizer(bool show) -{ - Show(show); - if (show) - manifold_warning_icon->Show(showing_manifold_warning_icon && show); -} - -void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) -{ - if ((showing_manifold_warning_icon = !warning_icon_name.empty())) { - m_warning_icon_name = warning_icon_name; - manifold_warning_icon->SetBitmap(*get_bmp_bundle(m_warning_icon_name)); - } -} - -enum SlicedInfoIdx -{ - siFilament_g, - siFilament_m, - siFilament_mm3, - siMateril_unit, - siCost, - siEstimatedTime, - siWTNumbetOfToolchanges, - - siCount -}; - -class SlicedInfo : public wxStaticBoxSizer -{ -public: - SlicedInfo(wxWindow *parent); - void SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label=""); - -private: - std::vector> info_vec; -}; - -SlicedInfo::SlicedInfo(wxWindow *parent) : - wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Sliced Info")), wxVERTICAL) -{ - GetStaticBox()->SetFont(wxGetApp().bold_font()); - wxGetApp().UpdateDarkUI(GetStaticBox()); - - auto *grid_sizer = new wxFlexGridSizer(2, 5, 15); - grid_sizer->SetFlexibleDirection(wxVERTICAL); - - info_vec.reserve(siCount); - - auto init_info_label = [this, parent, grid_sizer](wxString text_label) { - auto *text = new wxStaticText(parent, wxID_ANY, text_label); - text->SetFont(wxGetApp().small_font()); - auto info_label = new wxStaticText(parent, wxID_ANY, "N/A"); - info_label->SetFont(wxGetApp().small_font()); - grid_sizer->Add(text, 0); - grid_sizer->Add(info_label, 0); - info_vec.push_back(std::pair(text, info_label)); - }; - - init_info_label(_L("Used Filament (g)")); - init_info_label(_L("Used Filament (m)")); - init_info_label(_L("Used Filament (mm³)")); - init_info_label(_L("Used Material (unit)")); - init_info_label(_L("Cost (money)")); - init_info_label(_L("Estimated printing time")); - init_info_label(_L("Number of tool changes")); - - Add(grid_sizer, 0, wxEXPAND); - this->Show(false); -} - -void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label/*=""*/) -{ - const bool show = text != "N/A"; - if (show) - info_vec[idx].second->SetLabelText(text); - if (!new_label.IsEmpty()) - info_vec[idx].first->SetLabelText(new_label); - info_vec[idx].first->Show(show); - info_vec[idx].second->Show(show); -} - -// Frequently changed parameters - -class FreqChangedParams : public OG_Settings -{ - double m_brim_width = 0.0; - wxButton* m_wiping_dialog_button{ nullptr }; - wxSizer* m_sizer {nullptr}; - - std::shared_ptr m_og_sla; - std::vector m_empty_buttons; -public: - FreqChangedParams(wxWindow* parent); - ~FreqChangedParams() {} - - wxButton* get_wiping_dialog_button() { return m_wiping_dialog_button; } - wxSizer* get_sizer() override; - ConfigOptionsGroup* get_og(const bool is_fff); - void Show(const bool is_fff) override; - - void msw_rescale(); - void sys_color_changed(); -}; - -void FreqChangedParams::msw_rescale() -{ - m_og->msw_rescale(); - m_og_sla->msw_rescale(); -} - -void FreqChangedParams::sys_color_changed() -{ - m_og->sys_color_changed(); - m_og_sla->sys_color_changed(); - - for (auto btn: m_empty_buttons) - btn->sys_color_changed(); - - wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); -} - -FreqChangedParams::FreqChangedParams(wxWindow* parent) : - OG_Settings(parent, false) -{ - DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; - - // Frequently changed parameters for FFF_technology - m_og->set_config(config); - m_og->hide_labels(); - - m_og->on_change = [config, this](t_config_option_key opt_key, boost::any value) { - Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); - if (!tab_print) return; - - if (opt_key == "fill_density") { - tab_print->update_dirty(); - tab_print->reload_config(); - tab_print->update(); - } - else - { - DynamicPrintConfig new_conf = *config; - if (opt_key == "brim") { - double new_val; - double brim_width = config->opt_float("brim_width"); - if (boost::any_cast(value) == true) - { - new_val = m_brim_width == 0.0 ? 5 : - m_brim_width < 0.0 ? m_brim_width * (-1) : - m_brim_width; - } - else { - m_brim_width = brim_width * (-1); - new_val = 0; - } - new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); - } - else { - assert(opt_key == "support"); - const wxString& selection = boost::any_cast(value); - PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); - - auto support_material = selection == _("None") ? false : true; - new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); - - if (selection == _("Everywhere")) { - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); - if (printer_technology == ptFFF) - new_conf.set_key_value("support_material_auto", new ConfigOptionBool(true)); - } else if (selection == _("Support on build plate only")) { - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); - if (printer_technology == ptFFF) - new_conf.set_key_value("support_material_auto", new ConfigOptionBool(true)); - } else if (selection == _("For support enforcers only")) { - assert(printer_technology == ptFFF); - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); - new_conf.set_key_value("support_material_auto", new ConfigOptionBool(false)); - } - } - tab_print->load_config(new_conf); - } - }; - - - Line line = Line { "", "" }; - - ConfigOptionDef support_def; - support_def.label = L("Supports"); - support_def.type = coStrings; - support_def.tooltip = L("Select what kind of support do you need"); - support_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { - L("None"), - L("Support on build plate only"), - L("For support enforcers only"), - L("Everywhere") - }); - support_def.set_default_value(new ConfigOptionStrings{ "None" }); - Option option = Option(support_def, "support"); - option.opt.full_width = true; - line.append_option(option); - - /* Not a best solution, but - * Temporary workaround for right border alignment - */ - auto empty_widget = [this] (wxWindow* parent) { - auto sizer = new wxBoxSizer(wxHORIZONTAL); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, - wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); - sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); - m_empty_buttons.push_back(btn); - return sizer; - }; - line.append_widget(empty_widget); - - m_og->append_line(line); - - - line = Line { "", "" }; - - option = m_og->get_option("fill_density"); - option.opt.label = L("Infill"); - option.opt.width = 8; - option.opt.sidetext = " "; - line.append_option(option); - - m_brim_width = config->opt_float("brim_width"); - ConfigOptionDef def; - def.label = L("Brim"); - def.type = coBool; - def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); - def.gui_type = ConfigOptionDef::GUIType::undefined; - def.set_default_value(new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }); - option = Option(def, "brim"); - option.opt.sidetext = ""; - line.append_option(option); - - auto wiping_dialog_btn = [this](wxWindow* parent) { - m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _L("Purging volumes") + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - wxGetApp().SetWindowVariantForButton(m_wiping_dialog_button); - wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL); - m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) - { - auto &project_config = wxGetApp().preset_bundle->project_config; - const std::vector &init_matrix = (project_config.option("wiping_volumes_matrix"))->values; - const std::vector &init_extruders = (project_config.option("wiping_volumes_extruders"))->values; - - const std::vector extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); - - WipingDialog dlg(parent, cast(init_matrix), cast(init_extruders), extruder_colours); - - if (dlg.ShowModal() == wxID_OK) { - std::vector matrix = dlg.get_matrix(); - std::vector extruders = dlg.get_extruders(); - (project_config.option("wiping_volumes_matrix"))->values = std::vector(matrix.begin(), matrix.end()); - (project_config.option("wiping_volumes_extruders"))->values = std::vector(extruders.begin(), extruders.end()); - // Update Project dirty state, update application title bar. - wxGetApp().plater()->update_project_dirty_from_presets(); - wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); - } - })); - - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, - wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); - sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, - int(0.3 * wxGetApp().em_unit())); - m_empty_buttons.push_back(btn); - - return sizer; - }; - line.append_widget(wiping_dialog_btn); - m_og->append_line(line); - - m_og->activate(); - - Choice* choice = dynamic_cast(m_og->get_field("support")); - choice->suppress_scroll(); - - // Frequently changed parameters for SLA_technology - m_og_sla = std::make_shared(parent, ""); - m_og_sla->hide_labels(); - DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - m_og_sla->set_config(config_sla); - - m_og_sla->on_change = [config_sla](t_config_option_key opt_key, boost::any value) { - Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT); - if (!tab) return; - - DynamicPrintConfig new_conf = *config_sla; - if (opt_key == "pad") { - const wxString& selection = boost::any_cast(value); - - const bool pad_enable = selection == _("None") ? false : true; - new_conf.set_key_value("pad_enable", new ConfigOptionBool(pad_enable)); - - if (selection == _("Below object")) - new_conf.set_key_value("pad_around_object", new ConfigOptionBool(false)); - else if (selection == _("Around object")) - new_conf.set_key_value("pad_around_object", new ConfigOptionBool(true)); - } - else - { - assert(opt_key == "support"); - const wxString& selection = boost::any_cast(value); - - const bool supports_enable = selection == _("None") ? false : true; - new_conf.set_key_value("supports_enable", new ConfigOptionBool(supports_enable)); - - std::string treetype = get_sla_suptree_prefix(new_conf); - - if (selection == _("Everywhere")) { - new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(false)); - new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); - } - else if (selection == _("Support on build plate only")) { - new_conf.set_key_value(treetype + "support_buildplate_only", new ConfigOptionBool(true)); - new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(false)); - } - else if (selection == _("For support enforcers only")) { - new_conf.set_key_value("support_enforcers_only", new ConfigOptionBool(true)); - } - } - - tab->load_config(new_conf); - tab->update_dirty(); - }; - - line = Line{ "", "" }; - - ConfigOptionDef support_def_sla = support_def; - support_def_sla.set_default_value(new ConfigOptionStrings{ "None" }); - option = Option(support_def_sla, "support"); - option.opt.full_width = true; - line.append_option(option); - line.append_widget(empty_widget); - m_og_sla->append_line(line); - - line = Line{ "", "" }; - - ConfigOptionDef pad_def; - pad_def.label = L("Pad"); - pad_def.type = coStrings; - pad_def.tooltip = L("Select what kind of pad do you need"); - pad_def.set_enum_labels(ConfigOptionDef::GUIType::select_close, { - L("None"), - L("Below object"), - L("Around object") - }); - pad_def.set_default_value(new ConfigOptionStrings{ "Below object" }); - option = Option(pad_def, "pad"); - option.opt.full_width = true; - line.append_option(option); - line.append_widget(empty_widget); - - m_og_sla->append_line(line); - - m_og_sla->activate(); - choice = dynamic_cast(m_og_sla->get_field("support")); - choice->suppress_scroll(); - choice = dynamic_cast(m_og_sla->get_field("pad")); - choice->suppress_scroll(); - - m_sizer = new wxBoxSizer(wxVERTICAL); - m_sizer->Add(m_og->sizer, 0, wxEXPAND); - m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND); -} - - -wxSizer* FreqChangedParams::get_sizer() -{ - return m_sizer; -} - -void FreqChangedParams::Show(const bool is_fff) -{ - const bool is_wdb_shown = m_wiping_dialog_button->IsShown(); - m_og->Show(is_fff); - m_og_sla->Show(!is_fff); - - // correct showing of the FreqChangedParams sizer when m_wiping_dialog_button is hidden - if (is_fff && !is_wdb_shown) - m_wiping_dialog_button->Hide(); -} - -ConfigOptionsGroup* FreqChangedParams::get_og(const bool is_fff) -{ - return is_fff ? m_og.get() : m_og_sla.get(); -} - -// Sidebar / private - -enum class ActionButtonType : int { - abReslice, - abExport, - abSendGCode -}; - -struct Sidebar::priv -{ - Plater *plater; - - wxScrolledWindow *scrolled; - wxPanel* presets_panel; // Used for MSW better layouts - - ModeSizer *mode_sizer {nullptr}; - wxFlexGridSizer *sizer_presets; - PlaterPresetComboBox *combo_print; - std::vector combos_filament; - wxBoxSizer *sizer_filaments; - PlaterPresetComboBox *combo_sla_print; - PlaterPresetComboBox *combo_sla_material; - PlaterPresetComboBox *combo_printer; - - wxBoxSizer *sizer_params; - FreqChangedParams *frequently_changed_parameters{ nullptr }; - ObjectList *object_list{ nullptr }; - ObjectManipulation *object_manipulation{ nullptr }; - ObjectSettings *object_settings{ nullptr }; - ObjectLayers *object_layers{ nullptr }; - ObjectInfo *object_info; - SlicedInfo *sliced_info; - - wxButton *btn_export_gcode; - wxButton *btn_reslice; - ScalableButton *btn_send_gcode; - //ScalableButton *btn_eject_device; - ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) - - bool is_collapsed {false}; - - priv(Plater *plater) : plater(plater) {} - ~priv(); - - void show_preset_comboboxes(); - -#ifdef _WIN32 - wxString btn_reslice_tip; - void show_rich_tip(const wxString& tooltip, wxButton* btn); - void hide_rich_tip(wxButton* btn); -#endif -}; - -Sidebar::priv::~priv() -{ - delete object_manipulation; - delete object_settings; - delete frequently_changed_parameters; - delete object_layers; -} - -void Sidebar::priv::show_preset_comboboxes() -{ - const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; - - for (size_t i = 0; i < 4; ++i) - sizer_presets->Show(i, !showSLA); - - for (size_t i = 4; i < 8; ++i) - sizer_presets->Show(i, showSLA); - - frequently_changed_parameters->Show(!showSLA); - - scrolled->GetParent()->Layout(); - scrolled->Refresh(); -} - -#ifdef _WIN32 -using wxRichToolTipPopup = wxCustomBackgroundWindow; -static wxRichToolTipPopup* get_rtt_popup(wxButton* btn) -{ - auto children = btn->GetChildren(); - for (auto child : children) - if (child->IsShown()) - return dynamic_cast(child); - return nullptr; -} - -void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn) -{ - if (tooltip.IsEmpty()) - return; - wxRichToolTip tip(tooltip, ""); - tip.SetIcon(wxICON_NONE); - tip.SetTipKind(wxTipKind_BottomRight); - tip.SetTitleFont(wxGetApp().normal_font()); - tip.SetBackgroundColour(wxGetApp().get_window_default_clr()); - - tip.ShowFor(btn); - // Every call of the ShowFor() creates new RichToolTip and show it. - // Every one else are hidden. - // So, set a text color just for the shown rich tooltip - if (wxRichToolTipPopup* popup = get_rtt_popup(btn)) { - auto children = popup->GetChildren(); - for (auto child : children) { - child->SetForegroundColour(wxGetApp().get_label_clr_default()); - // we neen just first text line for out rich tooltip - return; - } - } -} - -void Sidebar::priv::hide_rich_tip(wxButton* btn) -{ - if (wxRichToolTipPopup* popup = get_rtt_popup(btn)) - popup->Dismiss(); -} -#endif - -// Sidebar / public - -Sidebar::Sidebar(Plater *parent) - : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(42 * wxGetApp().em_unit(), -1)), p(new priv(parent)) -{ - SetFont(wxGetApp().normal_font()); - p->scrolled = new wxScrolledWindow(this); -// p->scrolled->SetScrollbars(0, 100, 1, 2); // ys_DELETE_after_testing. pixelsPerUnitY = 100 from https://github.com/prusa3d/PrusaSlicer/commit/8f019e5fa992eac2c9a1e84311c990a943f80b01, - // but this cause the bad layout of the sidebar, when all infoboxes appear. - // As a result we can see the empty block at the bottom of the sidebar - // But if we set this value to 5, layout will be better - p->scrolled->SetScrollRate(0, 5); - -#ifndef __APPLE__ -#ifdef _WIN32 - wxGetApp().UpdateDarkUI(this); - wxGetApp().UpdateDarkUI(p->scrolled); -#else -// SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -#endif -#endif - - // Sizer in the scrolled area - auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); - p->scrolled->SetSizer(scrolled_sizer); - - // Sizer with buttons for mode changing - p->mode_sizer = new ModeSizer(p->scrolled, int(0.5 * wxGetApp().em_unit())); - - // The preset chooser - p->sizer_presets = new wxFlexGridSizer(10, 1, 1, 2); - p->sizer_presets->AddGrowableCol(0, 1); - p->sizer_presets->SetFlexibleDirection(wxBOTH); - - bool is_msw = false; -#ifdef __WINDOWS__ - p->scrolled->SetDoubleBuffered(true); - - p->presets_panel = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxGetApp().UpdateDarkUI(p->presets_panel); - p->presets_panel->SetSizer(p->sizer_presets); - - is_msw = true; -#else - p->presets_panel = p->scrolled; -#endif //__WINDOWS__ - - p->sizer_filaments = new wxBoxSizer(wxVERTICAL); - - const int margin_5 = int(0.5 * wxGetApp().em_unit());// 5; - - auto init_combo = [this, margin_5](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { - auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + ":"); - text->SetFont(wxGetApp().small_font()); - *combo = new PlaterPresetComboBox(p->presets_panel, preset_type); - - auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); - combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); - if ((*combo)->edit_btn) - combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, - int(0.3*wxGetApp().em_unit())); - - auto *sizer_presets = this->p->sizer_presets; - auto *sizer_filaments = this->p->sizer_filaments; - // Hide controls, which will be shown/hidden in respect to the printer technology - text->Show(preset_type == Preset::TYPE_PRINTER); - sizer_presets->Add(text, 0, wxALIGN_LEFT | wxEXPAND | wxRIGHT, 4); - if (! filament) { - combo_and_btn_sizer->ShowItems(preset_type == Preset::TYPE_PRINTER); - sizer_presets->Add(combo_and_btn_sizer, 0, wxEXPAND | -#ifdef __WXGTK3__ - wxRIGHT, margin_5); -#else - wxBOTTOM, 1); - (void)margin_5; // supress unused capture warning -#endif // __WXGTK3__ - } else { - sizer_filaments->Add(combo_and_btn_sizer, 0, wxEXPAND | -#ifdef __WXGTK3__ - wxRIGHT, margin_5); -#else - wxBOTTOM, 1); -#endif // __WXGTK3__ - (*combo)->set_extruder_idx(0); - sizer_filaments->ShowItems(false); - sizer_presets->Add(sizer_filaments, 1, wxEXPAND); - } - }; - - p->combos_filament.push_back(nullptr); - init_combo(&p->combo_print, _L("Print settings"), Preset::TYPE_PRINT, false); - init_combo(&p->combos_filament[0], _L("Filament"), Preset::TYPE_FILAMENT, true); - init_combo(&p->combo_sla_print, _L("SLA print settings"), Preset::TYPE_SLA_PRINT, false); - init_combo(&p->combo_sla_material, _L("SLA material"), Preset::TYPE_SLA_MATERIAL, false); - init_combo(&p->combo_printer, _L("Printer"), Preset::TYPE_PRINTER, false); - - p->sizer_params = new wxBoxSizer(wxVERTICAL); - - // Frequently changed parameters - p->frequently_changed_parameters = new FreqChangedParams(p->scrolled); - p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM -#ifdef __WXGTK3__ - | wxRIGHT -#endif // __WXGTK3__ - , wxOSX ? 1 : margin_5); - - // Object List - p->object_list = new ObjectList(p->scrolled); - p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND); - - // Object Manipulations - p->object_manipulation = new ObjectManipulation(p->scrolled); - p->object_manipulation->Hide(); - p->sizer_params->Add(p->object_manipulation->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); - - // Frequently Object Settings - p->object_settings = new ObjectSettings(p->scrolled); - p->object_settings->Hide(); - p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); - - // Object Layers - p->object_layers = new ObjectLayers(p->scrolled); - p->object_layers->Hide(); - p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); - - // Info boxes - p->object_info = new ObjectInfo(p->scrolled); - p->sliced_info = new SlicedInfo(p->scrolled); - - // Sizer in the scrolled area - if (p->mode_sizer) - scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL); - - int size_margin = wxGTK3 ? wxLEFT | wxRIGHT : wxLEFT; - - is_msw ? - scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | size_margin, margin_5) : - scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | size_margin, margin_5); - scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | size_margin, margin_5); - scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); - scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); - - // Buttons underneath the scrolled area - - // rescalable bitmap buttons "Send to printer" and "Remove device" - - auto init_scalable_btn = [this](ScalableButton** btn, const std::string& icon_name, wxString tooltip = wxEmptyString) - { -#ifdef __APPLE__ - int bmp_px_cnt = 16; -#else - int bmp_px_cnt = 32; -#endif //__APPLE__ - ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt); - *btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT); - wxGetApp().SetWindowVariantForButton((*btn)); - -#ifdef _WIN32 - (*btn)->Bind(wxEVT_ENTER_WINDOW, [tooltip, btn, this](wxMouseEvent& event) { - p->show_rich_tip(tooltip, *btn); - event.Skip(); - }); - (*btn)->Bind(wxEVT_LEAVE_WINDOW, [btn, this](wxMouseEvent& event) { - p->hide_rich_tip(*btn); - event.Skip(); - }); -#else - (*btn)->SetToolTip(tooltip); -#endif // _WIN32 - (*btn)->Hide(); - }; - - init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + " " +GUI::shortkey_ctrl_prefix() + "Shift+G"); -// init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device ") + GUI::shortkey_ctrl_prefix() + "T"); - init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + " " + GUI::shortkey_ctrl_prefix() + "U"); - - // regular buttons "Slice now" and "Export G-code" - -// const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4; -#ifdef _WIN32 - const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); -#else - const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4; -#endif - auto init_btn = [this](wxButton **btn, wxString label, const int button_height) { - *btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition, - wxSize(-1, button_height), wxBU_EXACTFIT); - wxGetApp().SetWindowVariantForButton((*btn)); - (*btn)->SetFont(wxGetApp().bold_font()); - wxGetApp().UpdateDarkUI((*btn), true); - }; - - init_btn(&p->btn_export_gcode, _L("Export G-code") + dots , scaled_height); - init_btn(&p->btn_reslice , _L("Slice now") , scaled_height); - - enable_buttons(false); - - auto *btns_sizer = new wxBoxSizer(wxHORIZONTAL); - - auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL); - complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); - complect_btns_sizer->Add(p->btn_send_gcode, 0, wxLEFT, margin_5); - complect_btns_sizer->Add(p->btn_export_gcode_removable, 0, wxLEFT, margin_5); -// complect_btns_sizer->Add(p->btn_eject_device); - - - btns_sizer->Add(p->btn_reslice, 1, wxEXPAND | wxTOP | wxBOTTOM, margin_5); - btns_sizer->Add(complect_btns_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, margin_5); - - auto *sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(p->scrolled, 1, wxEXPAND); - sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, margin_5); - SetSizer(sizer); - - // Events - p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); - p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) - { - if (p->plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) - return; - - const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); - if (export_gcode_after_slicing) - p->plater->export_gcode(true); - else - p->plater->reslice(); - p->plater->select_view_3D("Preview"); - }); - -#ifdef _WIN32 - p->btn_reslice->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) { - p->show_rich_tip(p->btn_reslice_tip, p->btn_reslice); - event.Skip(); - }); - p->btn_reslice->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { - p->hide_rich_tip(p->btn_reslice); - event.Skip(); - }); -#endif // _WIN32 - - p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); -// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); - p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); -} - -Sidebar::~Sidebar() {} - -void Sidebar::init_filament_combo(PlaterPresetComboBox** combo, const int extr_idx) -{ - *combo = new PlaterPresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); - (*combo)->set_extruder_idx(extr_idx); - - auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); - combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); - combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, - int(0.3*wxGetApp().em_unit())); - - this->p->sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | -#ifdef __WXGTK3__ - wxRIGHT, int(0.5 * wxGetApp().em_unit())); -#else - wxBOTTOM, 1); -#endif // __WXGTK3__ -} - -void Sidebar::remove_unused_filament_combos(const size_t current_extruder_count) -{ - if (current_extruder_count >= p->combos_filament.size()) - return; - auto sizer_filaments = this->p->sizer_filaments; - while (p->combos_filament.size() > current_extruder_count) { - const int last = p->combos_filament.size() - 1; - sizer_filaments->Remove(last); - (*p->combos_filament[last]).Destroy(); - p->combos_filament.pop_back(); - } -} - -void Sidebar::update_all_preset_comboboxes() -{ - PresetBundle &preset_bundle = *wxGetApp().preset_bundle; - const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); - - // Update the print choosers to only contain the compatible presets, update the dirty flags. - if (print_tech == ptFFF) - p->combo_print->update(); - else { - p->combo_sla_print->update(); - p->combo_sla_material->update(); - } - // Update the printer choosers, update the dirty flags. - p->combo_printer->update(); - // Update the filament choosers to only contain the compatible presets, update the color preview, - // update the dirty flags. - if (print_tech == ptFFF) { - for (PlaterPresetComboBox* cb : p->combos_filament) - cb->update(); - } -} - -void Sidebar::update_presets(Preset::Type preset_type) -{ - PresetBundle &preset_bundle = *wxGetApp().preset_bundle; - const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); - - switch (preset_type) { - case Preset::TYPE_FILAMENT: - { - const size_t extruder_cnt = print_tech != ptFFF ? 1 : - dynamic_cast(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); - const size_t filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); - - for (size_t i = 0; i < filament_cnt; i++) - p->combos_filament[i]->update(); - - break; - } - - case Preset::TYPE_PRINT: - p->combo_print->update(); - break; - - case Preset::TYPE_SLA_PRINT: - p->combo_sla_print->update(); - break; - - case Preset::TYPE_SLA_MATERIAL: - p->combo_sla_material->update(); - break; - - case Preset::TYPE_PRINTER: - { - update_all_preset_comboboxes(); -#if 1 // #ysFIXME_delete_after_test_of >> it looks like CallAfter() is no need [issue with disapearing of comboboxes are not reproducible] - p->show_preset_comboboxes(); -#else - // CallAfter is really needed here to correct layout of the preset comboboxes, - // when printer technology is changed during a project loading AND/OR switching the application mode. - // Otherwise, some of comboboxes are invisible - CallAfter([this]() { p->show_preset_comboboxes(); }); -#endif - break; - } - - default: break; - } - - // Synchronize config.ini with the current selections. - wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); -} - -void Sidebar::update_mode_sizer() const -{ - if (p->mode_sizer) - p->mode_sizer->SetMode(m_mode); -} - -void Sidebar::change_top_border_for_mode_sizer(bool increase_border) -{ - if (p->mode_sizer) { - p->mode_sizer->set_items_flag(increase_border ? wxTOP : 0); - p->mode_sizer->set_items_border(increase_border ? int(0.5 * wxGetApp().em_unit()) : 0); - } -} - -void Sidebar::update_reslice_btn_tooltip() const -{ - wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]"; - if (m_mode != comSimple) - tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code"); -#ifdef _WIN32 - p->btn_reslice_tip = tooltip; -#else - p->btn_reslice->SetToolTip(tooltip); -#endif -} - -void Sidebar::msw_rescale() -{ - SetMinSize(wxSize(42 * wxGetApp().em_unit(), -1)); - - for (PlaterPresetComboBox* combo : std::vector { p->combo_print, - p->combo_sla_print, - p->combo_sla_material, - p->combo_printer } ) - combo->msw_rescale(); - for (PlaterPresetComboBox* combo : p->combos_filament) - combo->msw_rescale(); - - p->frequently_changed_parameters->msw_rescale(); - p->object_list->msw_rescale(); - p->object_manipulation->msw_rescale(); - p->object_layers->msw_rescale(); - -#ifdef _WIN32 - const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); -#else - const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4; -#endif - p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); - p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); - - p->scrolled->Layout(); -} - -void Sidebar::sys_color_changed() -{ -#ifdef _WIN32 - wxWindowUpdateLocker noUpdates(this); - - for (wxWindow* win : std::vector{ this, p->sliced_info->GetStaticBox(), p->object_info->GetStaticBox(), p->btn_reslice, p->btn_export_gcode }) - wxGetApp().UpdateDarkUI(win); - for (wxWindow* win : std::vector{ p->scrolled, p->presets_panel }) - wxGetApp().UpdateAllStaticTextDarkUI(win); - for (wxWindow* btn : std::vector{ p->btn_reslice, p->btn_export_gcode }) - wxGetApp().UpdateDarkUI(btn, true); - - if (p->mode_sizer) - p->mode_sizer->sys_color_changed(); - p->frequently_changed_parameters->sys_color_changed(); - p->object_settings->sys_color_changed(); -#endif - - for (PlaterPresetComboBox* combo : std::vector{ p->combo_print, - p->combo_sla_print, - p->combo_sla_material, - p->combo_printer }) - combo->sys_color_changed(); - for (PlaterPresetComboBox* combo : p->combos_filament) - combo->sys_color_changed(); - - p->object_list->sys_color_changed(); - p->object_manipulation->sys_color_changed(); - p->object_layers->sys_color_changed(); - - // btn...->msw_rescale() updates icon on button, so use it - p->btn_send_gcode->sys_color_changed(); -// p->btn_eject_device->msw_rescale(); - p->btn_export_gcode_removable->sys_color_changed(); - - p->scrolled->Layout(); - p->scrolled->Refresh(); -} - -void Sidebar::update_mode_markers() -{ - if (p->mode_sizer) - p->mode_sizer->update_mode_markers(); -} - -ObjectManipulation* Sidebar::obj_manipul() -{ - return p->object_manipulation; -} - -ObjectList* Sidebar::obj_list() -{ - return p->object_list; -} - -ObjectSettings* Sidebar::obj_settings() -{ - return p->object_settings; -} - -ObjectLayers* Sidebar::obj_layers() -{ - return p->object_layers; -} - -wxScrolledWindow* Sidebar::scrolled_panel() -{ - return p->scrolled; -} - -wxPanel* Sidebar::presets_panel() -{ - return p->presets_panel; -} - -ConfigOptionsGroup* Sidebar::og_freq_chng_params(const bool is_fff) -{ - return p->frequently_changed_parameters->get_og(is_fff); -} - -wxButton* Sidebar::get_wiping_dialog_button() -{ - return p->frequently_changed_parameters->get_wiping_dialog_button(); -} - -void Sidebar::update_objects_list_extruder_column(size_t extruders_count) -{ - p->object_list->update_objects_list_extruder_column(extruders_count); -} - -void Sidebar::show_info_sizer() -{ - Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - ModelObjectPtrs objects = p->plater->model().objects; - const int obj_idx = selection.get_object_idx(); - const int inst_idx = selection.get_instance_idx(); - - if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx || - inst_idx < 0 || int(objects[obj_idx]->instances.size()) <= inst_idx || - objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed - (selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) || - !(selection.is_single_full_instance() || selection.is_single_volume())) { - p->object_info->Show(false); - return; - } - - const ModelObject* model_object = objects[obj_idx]; - - bool imperial_units = wxGetApp().app_config->get_bool("use_inches"); - double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f; - - ModelVolume* vol = nullptr; - Transform3d t; - if (selection.is_single_volume()) { - std::vector obj_idxs, vol_idxs; - wxGetApp().obj_list()->get_selection_indexes(obj_idxs, vol_idxs); - if (vol_idxs.size() != 1) - // Case when this fuction is called between update selection in ObjectList and on Canvas - // Like after try to delete last solid part in object, the object is selected in ObjectLIst when just a part is still selected on Canvas - // see https://github.com/prusa3d/PrusaSlicer/issues/7408 - return; - vol = model_object->volumes[vol_idxs[0]]; - t = model_object->instances[inst_idx]->get_matrix() * vol->get_matrix(); - } - - Vec3d size = vol ? vol->mesh().transformed_bounding_box(t).size() : model_object->instance_bounding_box(inst_idx).size(); - p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f", size(0)*koef, size(1)*koef, size(2)*koef)); -// p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); - - const TriangleMeshStats& stats = vol ? vol->mesh().stats() : model_object->get_object_stl_stats(); - - double volume_val = stats.volume; - if (vol) - volume_val *= std::fabs(t.matrix().block(0, 0, 3, 3).determinant()); - - p->object_info->info_volume->SetLabel(wxString::Format("%.2f", volume_val * pow(koef,3))); - p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts), - static_cast(model_object->facets_count()), stats.number_of_parts)); - - wxString info_manifold_label; - auto mesh_errors = obj_list()->get_mesh_errors_info(&info_manifold_label); - wxString tooltip = mesh_errors.tooltip; - p->object_info->update_warning_icon(mesh_errors.warning_icon_name); - p->object_info->info_manifold->SetLabel(info_manifold_label); - p->object_info->info_manifold->SetToolTip(tooltip); - p->object_info->manifold_warning_icon->SetToolTip(tooltip); - - p->object_info->show_sizer(true); - if (vol || model_object->volumes.size() == 1) - p->object_info->info_icon->Hide(); - - if (p->plater->printer_technology() == ptSLA) { - for (auto item: p->object_info->sla_hidden_items) - item->Show(false); - } -} - -void Sidebar::update_sliced_info_sizer() -{ - if (p->sliced_info->IsShown(size_t(0))) - { - if (p->plater->printer_technology() == ptSLA) - { - const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); - wxString new_label = _L("Used Material (ml)") + ":"; - const bool is_supports = ps.support_used_material > 0.0; - if (is_supports) - new_label += format_wxstr("\n - %s\n - %s", _L_PLURAL("object", "objects", p->plater->model().objects.size()), _L("supports and pad")); - - wxString info_text = is_supports ? - wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000, - ps.objects_used_material / 1000, - ps.support_used_material / 1000) : - wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000); - p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); - - wxString str_total_cost = "N/A"; - - DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_SLA_MATERIAL)->get_config(); - if (cfg->option("bottle_cost")->getFloat() > 0.0 && - cfg->option("bottle_volume")->getFloat() > 0.0) - { - double material_cost = cfg->option("bottle_cost")->getFloat() / - cfg->option("bottle_volume")->getFloat(); - str_total_cost = wxString::Format("%.3f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); - } - p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost"); - - wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : from_u8(short_time_ui(get_time_dhms(float(ps.estimated_print_time)))); - p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":"); - - p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed()); - - // Hide non-SLA sliced info parameters - p->sliced_info->SetTextAndShow(siFilament_m, "N/A"); - p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A"); - p->sliced_info->SetTextAndShow(siFilament_g, "N/A"); - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, "N/A"); - } - else - { - const PrintStatistics& ps = p->plater->fff_print().print_statistics(); - const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; - - bool imperial_units = wxGetApp().app_config->get_bool("use_inches"); - double koef = imperial_units ? ObjectManipulation::in_to_mm : 1000.0; - - wxString new_label = imperial_units ? _L("Used Filament (in)") : _L("Used Filament (m)"); - if (is_wipe_tower) - new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower")); - - wxString info_text = is_wipe_tower ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / koef, - (ps.total_used_filament - ps.total_wipe_tower_filament) / koef, - ps.total_wipe_tower_filament / koef) : - wxString::Format("%.2f", ps.total_used_filament / koef); - p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); - - koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f; - new_label = imperial_units ? _L("Used Filament (in³)") : _L("Used Filament (mm³)"); - info_text = wxString::Format("%.2f", imperial_units ? ps.total_extruded_volume * koef : ps.total_extruded_volume); - p->sliced_info->SetTextAndShow(siFilament_mm3, info_text, new_label); - - if (ps.total_weight == 0.0) - p->sliced_info->SetTextAndShow(siFilament_g, "N/A"); - else { - new_label = _L("Used Filament (g)"); - info_text = wxString::Format("%.2f", ps.total_weight); - - if (ps.filament_stats.size() > 1) - new_label += ":"; - - const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; - for (const auto& [filament_id, filament_vol] : ps.filament_stats) { - assert(filament_id < extruders_filaments.size()); - if (const Preset* preset = extruders_filaments[filament_id].get_selected_preset()) { - double filament_weight; - if (ps.filament_stats.size() == 1) - filament_weight = ps.total_weight; - else { - double filament_density = preset->config.opt_float("filament_density", 0); - filament_weight = filament_vol * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter; - - new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament_id + 1); - info_text += wxString::Format("\n%.2f", filament_weight); - } - - double spool_weight = preset->config.opt_float("filament_spool_weight", 0); - if (spool_weight != 0.0) { - new_label += "\n " + _L("(including spool)"); - info_text += wxString::Format(" (%.2f)\n", filament_weight + spool_weight); - } - } - } - - p->sliced_info->SetTextAndShow(siFilament_g, info_text, new_label); - } - - new_label = _L("Cost"); - if (is_wipe_tower) - new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower")); - - info_text = ps.total_cost == 0.0 ? "N/A" : - is_wipe_tower ? - wxString::Format("%.2f \n%.2f \n%.2f", ps.total_cost, - (ps.total_cost - ps.total_wipe_tower_cost), - ps.total_wipe_tower_cost) : - wxString::Format("%.2f", ps.total_cost); - p->sliced_info->SetTextAndShow(siCost, info_text, new_label); - - if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") - p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); - else { - info_text = ""; - new_label = _L("Estimated printing time") + ":"; - if (ps.estimated_normal_print_time != "N/A") { - new_label += format_wxstr("\n - %1%", _L("normal mode")); - info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_normal_print_time)); - - p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed()); - - } - if (ps.estimated_silent_print_time != "N/A") { - new_label += format_wxstr("\n - %1%", _L("stealth mode")); - info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_silent_print_time)); - } - p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); - } - - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, ps.total_toolchanges > 0 ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); - - // Hide non-FFF sliced info parameters - p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); - } - } - - Layout(); -} - -void Sidebar::show_sliced_info_sizer(const bool show) -{ - wxWindowUpdateLocker freeze_guard(this); - - p->sliced_info->Show(show); - if (show) - update_sliced_info_sizer(); - - Layout(); - p->scrolled->Refresh(); -} - -void Sidebar::enable_buttons(bool enable) -{ - p->btn_reslice->Enable(enable); - p->btn_export_gcode->Enable(enable); - p->btn_send_gcode->Enable(enable); -// p->btn_eject_device->Enable(enable); - p->btn_export_gcode_removable->Enable(enable); -} - -bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } -bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } -bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } -bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); } -//bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); } -//bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); } - -bool Sidebar::is_multifilament() -{ - return p->combos_filament.size() > 1; -} - -void Sidebar::update_mode() -{ - m_mode = wxGetApp().get_mode(); - - update_reslice_btn_tooltip(); - update_mode_sizer(); - - wxWindowUpdateLocker noUpdates(this); - - if (m_mode == comSimple) - p->object_manipulation->set_coordinates_type(ECoordinatesType::World); - - p->object_list->get_sizer()->Show(m_mode > comSimple); - - p->object_list->unselect_objects(); - p->object_list->update_selections(); - - Layout(); -} - -bool Sidebar::is_collapsed() { return p->is_collapsed; } - -void Sidebar::collapse(bool collapse) -{ - p->is_collapsed = collapse; - - this->Show(!collapse); - p->plater->Layout(); - - // save collapsing state to the AppConfig - if (wxGetApp().is_editor()) - wxGetApp().app_config->set("collapsed_sidebar", collapse ? "1" : "0"); -} - -#ifdef _MSW_DARK_MODE -void Sidebar::show_mode_sizer(bool show) -{ - p->mode_sizer->Show(show); -} -#endif - -void Sidebar::update_ui_from_settings() -{ - p->object_manipulation->update_ui_from_settings(); - show_info_sizer(); - update_sliced_info_sizer(); - // update Cut gizmo, if it's open - p->plater->canvas3D()->update_gizmos_on_off_state(); - p->plater->set_current_canvas_as_dirty(); - p->plater->get_current_canvas3D()->request_extra_frame(); - p->object_list->apply_volumes_order(); -} - -std::vector& Sidebar::combos_filament() -{ - return p->combos_filament; -} - // Plater::DropTarget class PlaterDropTarget : public wxFileDropTarget @@ -5222,16 +3817,6 @@ void Plater::priv::bring_instance_forward() const } } -void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const -{ - switch (btn_type) - { - case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break; - case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break; - case ActionButtonType::abSendGCode: /*p->btn_send_gcode->SetLabelText(label);*/ break; - } -} - // Plater / Public Plater::Plater(wxWindow *parent, MainFrame *main_frame) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ca111fdf5a..3f751b41ba 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -23,20 +23,13 @@ #include +#include "Sidebar.hpp" #include "Selection.hpp" -#include "libslic3r/enum_bitmask.hpp" -#include "libslic3r/Preset.hpp" -#include "libslic3r/BoundingBox.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "Jobs/Job.hpp" #include "Jobs/Worker.hpp" -#include "GUI.hpp" -#include "I18N.hpp" -class wxButton; -class ScalableButton; -class wxScrolledWindow; class wxString; namespace Slic3r { @@ -62,82 +55,11 @@ namespace UndoRedo { namespace GUI { class MainFrame; -class ConfigOptionsGroup; -class ObjectManipulation; -class ObjectSettings; -class ObjectLayers; -class ObjectList; class GLCanvas3D; class Mouse3DController; class NotificationManager; struct Camera; class GLToolbar; -class PlaterPresetComboBox; - -using t_optgroups = std::vector >; - -class Plater; -enum class ActionButtonType : int; - -class Sidebar : public wxPanel -{ - ConfigOptionMode m_mode{ConfigOptionMode::comSimple}; -public: - Sidebar(Plater *parent); - Sidebar(Sidebar &&) = delete; - Sidebar(const Sidebar &) = delete; - Sidebar &operator=(Sidebar &&) = delete; - Sidebar &operator=(const Sidebar &) = delete; - ~Sidebar(); - - void init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx); - void remove_unused_filament_combos(const size_t current_extruder_count); - void update_all_preset_comboboxes(); - void update_presets(Slic3r::Preset::Type preset_type); - void update_mode_sizer() const; - void change_top_border_for_mode_sizer(bool increase_border); - void update_reslice_btn_tooltip() const; - void msw_rescale(); - void sys_color_changed(); - void update_mode_markers(); - - ObjectManipulation* obj_manipul(); - ObjectList* obj_list(); - ObjectSettings* obj_settings(); - ObjectLayers* obj_layers(); - wxScrolledWindow* scrolled_panel(); - wxPanel* presets_panel(); - - ConfigOptionsGroup* og_freq_chng_params(const bool is_fff); - wxButton* get_wiping_dialog_button(); - void update_objects_list_extruder_column(size_t extruders_count); - void show_info_sizer(); - void show_sliced_info_sizer(const bool show); - void update_sliced_info_sizer(); - void enable_buttons(bool enable); - void set_btn_label(const ActionButtonType btn_type, const wxString& label) const; - bool show_reslice(bool show) const; - bool show_export(bool show) const; - bool show_send(bool show) const; - bool show_eject(bool show)const; - bool show_export_removable(bool show) const; - bool get_eject_shown() const; - bool is_multifilament(); - void update_mode(); - bool is_collapsed(); - void collapse(bool collapse); - void update_ui_from_settings(); - -#ifdef _MSW_DARK_MODE - void show_mode_sizer(bool show); -#endif - - std::vector& combos_filament(); - -private: - struct priv; - std::unique_ptr p; -}; class Plater: public wxPanel { diff --git a/src/slic3r/GUI/Sidebar.cpp b/src/slic3r/GUI/Sidebar.cpp new file mode 100644 index 0000000000..cbc0d47505 --- /dev/null +++ b/src/slic3r/GUI/Sidebar.cpp @@ -0,0 +1,1141 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2022 Michael Kirsch +///|/ Copyright (c) 2021 Boleslaw Ciesielski +///|/ Copyright (c) 2019 John Drake @foxox +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#include "Sidebar.hpp" +#include "FrequentlyChangedParameters.hpp" +#include "Plater.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#include +#endif + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/PresetBundle.hpp" + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "GUI_ObjectManipulation.hpp" +#include "GUI_ObjectLayers.hpp" +#include "wxExtensions.hpp" +#include "format.hpp" +#include "Selection.hpp" +#include "Tab.hpp" +#include "I18N.hpp" + +#include "NotificationManager.hpp" +#include "PresetComboBoxes.hpp" +#include "MsgDialog.hpp" + +using Slic3r::Preset; +using Slic3r::GUI::format_wxstr; + +namespace Slic3r { +namespace GUI { + +class ObjectInfo : public wxStaticBoxSizer +{ + std::string m_warning_icon_name{ "exclamation" }; +public: + ObjectInfo(wxWindow *parent); + + wxStaticBitmap *manifold_warning_icon; + wxStaticBitmap *info_icon; + wxStaticText *info_size; + wxStaticText *info_volume; + wxStaticText *info_facets; + wxStaticText *info_manifold; + + wxStaticText *label_volume; + std::vector sla_hidden_items; + + bool showing_manifold_warning_icon; + void show_sizer(bool show); + void update_warning_icon(const std::string& warning_icon_name); +}; + +ObjectInfo::ObjectInfo(wxWindow *parent) : + wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Info")), wxVERTICAL) +{ + GetStaticBox()->SetFont(wxGetApp().bold_font()); + wxGetApp().UpdateDarkUI(GetStaticBox()); + + auto *grid_sizer = new wxFlexGridSizer(4, 5, 15); + grid_sizer->SetFlexibleDirection(wxHORIZONTAL); + + auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label, wxSizer* sizer_with_icon=nullptr) { + auto *text = new wxStaticText(parent, wxID_ANY, text_label + ":"); + text->SetFont(wxGetApp().small_font()); + *info_label = new wxStaticText(parent, wxID_ANY, ""); + (*info_label)->SetFont(wxGetApp().small_font()); + grid_sizer->Add(text, 0); + if (sizer_with_icon) { + sizer_with_icon->Insert(0, *info_label, 0); + grid_sizer->Add(sizer_with_icon, 0, wxEXPAND); + } + else + grid_sizer->Add(*info_label, 0); + return text; + }; + + init_info_label(&info_size, _L("Size")); + + info_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle("info")); + info_icon->SetToolTip(_L("For a multipart object, this value isn't accurate.\n" + "It doesn't take account of intersections and negative volumes.")); + auto* volume_info_sizer = new wxBoxSizer(wxHORIZONTAL); + volume_info_sizer->Add(info_icon, 0, wxLEFT, 10); + label_volume = init_info_label(&info_volume, _L("Volume"), volume_info_sizer); + + init_info_label(&info_facets, _L("Facets")); +// label_materials = init_info_label(&info_materials, _L("Materials")); + Add(grid_sizer, 0, wxEXPAND); + + info_manifold = new wxStaticText(parent, wxID_ANY, ""); + info_manifold->SetFont(wxGetApp().small_font()); + manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle(m_warning_icon_name)); + auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); + sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); + sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); + Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4); + + sla_hidden_items = { label_volume, info_volume, /*label_materials, info_materials*/ }; + + // Fixes layout issues on plater, short BitmapComboBoxes with some Windows scaling, see GH issue #7414. + this->Show(false); +} + +void ObjectInfo::show_sizer(bool show) +{ + Show(show); + if (show) + manifold_warning_icon->Show(showing_manifold_warning_icon && show); +} + +void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) +{ + if ((showing_manifold_warning_icon = !warning_icon_name.empty())) { + m_warning_icon_name = warning_icon_name; + manifold_warning_icon->SetBitmap(*get_bmp_bundle(m_warning_icon_name)); + } +} + +enum SlicedInfoIdx +{ + siFilament_g, + siFilament_m, + siFilament_mm3, + siMateril_unit, + siCost, + siEstimatedTime, + siWTNumbetOfToolchanges, + + siCount +}; + +class SlicedInfo : public wxStaticBoxSizer +{ +public: + SlicedInfo(wxWindow *parent); + void SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label=""); + +private: + std::vector> info_vec; +}; + +SlicedInfo::SlicedInfo(wxWindow *parent) : + wxStaticBoxSizer(new wxStaticBox(parent, wxID_ANY, _L("Sliced Info")), wxVERTICAL) +{ + GetStaticBox()->SetFont(wxGetApp().bold_font()); + wxGetApp().UpdateDarkUI(GetStaticBox()); + + auto *grid_sizer = new wxFlexGridSizer(2, 5, 15); + grid_sizer->SetFlexibleDirection(wxVERTICAL); + + info_vec.reserve(siCount); + + auto init_info_label = [this, parent, grid_sizer](wxString text_label) { + auto *text = new wxStaticText(parent, wxID_ANY, text_label); + text->SetFont(wxGetApp().small_font()); + auto info_label = new wxStaticText(parent, wxID_ANY, "N/A"); + info_label->SetFont(wxGetApp().small_font()); + grid_sizer->Add(text, 0); + grid_sizer->Add(info_label, 0); + info_vec.push_back(std::pair(text, info_label)); + }; + + init_info_label(_L("Used Filament (g)")); + init_info_label(_L("Used Filament (m)")); + init_info_label(_L("Used Filament (mm³)")); + init_info_label(_L("Used Material (unit)")); + init_info_label(_L("Cost (money)")); + init_info_label(_L("Estimated printing time")); + init_info_label(_L("Number of tool changes")); + + Add(grid_sizer, 0, wxEXPAND); + this->Show(false); +} + +void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const wxString& new_label/*=""*/) +{ + const bool show = text != "N/A"; + if (show) + info_vec[idx].second->SetLabelText(text); + if (!new_label.IsEmpty()) + info_vec[idx].first->SetLabelText(new_label); + info_vec[idx].first->Show(show); + info_vec[idx].second->Show(show); +} + +// Sidebar / private + +struct Sidebar::priv +{ + Plater *plater; + + wxScrolledWindow *scrolled; + wxPanel* presets_panel; // Used for MSW better layouts + + ModeSizer *mode_sizer {nullptr}; + wxFlexGridSizer *sizer_presets; + PlaterPresetComboBox *combo_print; + std::vector combos_filament; + wxBoxSizer *sizer_filaments; + PlaterPresetComboBox *combo_sla_print; + PlaterPresetComboBox *combo_sla_material; + PlaterPresetComboBox *combo_printer; + + wxBoxSizer *sizer_params; + FreqChangedParams *frequently_changed_parameters{ nullptr }; + ObjectList *object_list{ nullptr }; + ObjectManipulation *object_manipulation{ nullptr }; + ObjectSettings *object_settings{ nullptr }; + ObjectLayers *object_layers{ nullptr }; + ObjectInfo *object_info; + SlicedInfo *sliced_info; + + wxButton *btn_export_gcode; + wxButton *btn_reslice; + ScalableButton *btn_send_gcode; + //ScalableButton *btn_eject_device; + ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected) + + bool is_collapsed {false}; + + priv(Plater *plater) : plater(plater) {} + ~priv(); + + void show_preset_comboboxes(); + +#ifdef _WIN32 + wxString btn_reslice_tip; + void show_rich_tip(const wxString& tooltip, wxButton* btn); + void hide_rich_tip(wxButton* btn); +#endif +}; + +Sidebar::priv::~priv() +{ + delete object_manipulation; + delete object_settings; + delete frequently_changed_parameters; + delete object_layers; +} + +void Sidebar::priv::show_preset_comboboxes() +{ + const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; + + for (size_t i = 0; i < 4; ++i) + sizer_presets->Show(i, !showSLA); + + for (size_t i = 4; i < 8; ++i) + sizer_presets->Show(i, showSLA); + + frequently_changed_parameters->Show(!showSLA); + + scrolled->GetParent()->Layout(); + scrolled->Refresh(); +} + +#ifdef _WIN32 +using wxRichToolTipPopup = wxCustomBackgroundWindow; +static wxRichToolTipPopup* get_rtt_popup(wxButton* btn) +{ + auto children = btn->GetChildren(); + for (auto child : children) + if (child->IsShown()) + return dynamic_cast(child); + return nullptr; +} + +void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn) +{ + if (tooltip.IsEmpty()) + return; + wxRichToolTip tip(tooltip, ""); + tip.SetIcon(wxICON_NONE); + tip.SetTipKind(wxTipKind_BottomRight); + tip.SetTitleFont(wxGetApp().normal_font()); + tip.SetBackgroundColour(wxGetApp().get_window_default_clr()); + + tip.ShowFor(btn); + // Every call of the ShowFor() creates new RichToolTip and show it. + // Every one else are hidden. + // So, set a text color just for the shown rich tooltip + if (wxRichToolTipPopup* popup = get_rtt_popup(btn)) { + auto children = popup->GetChildren(); + for (auto child : children) { + child->SetForegroundColour(wxGetApp().get_label_clr_default()); + // we neen just first text line for out rich tooltip + return; + } + } +} + +void Sidebar::priv::hide_rich_tip(wxButton* btn) +{ + if (wxRichToolTipPopup* popup = get_rtt_popup(btn)) + popup->Dismiss(); +} +#endif + +// Sidebar / public + +Sidebar::Sidebar(Plater *parent) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(42 * wxGetApp().em_unit(), -1)), p(new priv(parent)) +{ + p->scrolled = new wxScrolledWindow(this); +// p->scrolled->SetScrollbars(0, 100, 1, 2); // ys_DELETE_after_testing. pixelsPerUnitY = 100 from https://github.com/prusa3d/PrusaSlicer/commit/8f019e5fa992eac2c9a1e84311c990a943f80b01, + // but this cause the bad layout of the sidebar, when all infoboxes appear. + // As a result we can see the empty block at the bottom of the sidebar + // But if we set this value to 5, layout will be better + p->scrolled->SetScrollRate(0, 5); + + SetFont(wxGetApp().normal_font()); +#ifndef __APPLE__ +#ifdef _WIN32 + wxGetApp().UpdateDarkUI(this); + wxGetApp().UpdateDarkUI(p->scrolled); +#else + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif +#endif + + // Sizer in the scrolled area + auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); + p->scrolled->SetSizer(scrolled_sizer); + + // Sizer with buttons for mode changing + p->mode_sizer = new ModeSizer(p->scrolled, int(0.5 * wxGetApp().em_unit())); + + // The preset chooser + p->sizer_presets = new wxFlexGridSizer(10, 1, 1, 2); + p->sizer_presets->AddGrowableCol(0, 1); + p->sizer_presets->SetFlexibleDirection(wxBOTH); + + bool is_msw = false; +#ifdef __WINDOWS__ + p->scrolled->SetDoubleBuffered(true); + + p->presets_panel = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + wxGetApp().UpdateDarkUI(p->presets_panel); + p->presets_panel->SetSizer(p->sizer_presets); + + is_msw = true; +#else + p->presets_panel = p->scrolled; +#endif //__WINDOWS__ + + p->sizer_filaments = new wxBoxSizer(wxVERTICAL); + + const int margin_5 = int(0.5 * wxGetApp().em_unit());// 5; + + auto init_combo = [this, margin_5](PlaterPresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) { + auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + ":"); + text->SetFont(wxGetApp().small_font()); + *combo = new PlaterPresetComboBox(p->presets_panel, preset_type); + + auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); + if ((*combo)->edit_btn) + combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, + int(0.3*wxGetApp().em_unit())); + + auto *sizer_presets = this->p->sizer_presets; + auto *sizer_filaments = this->p->sizer_filaments; + // Hide controls, which will be shown/hidden in respect to the printer technology + text->Show(preset_type == Preset::TYPE_PRINTER); + sizer_presets->Add(text, 0, wxALIGN_LEFT | wxEXPAND | wxRIGHT, 4); + if (! filament) { + combo_and_btn_sizer->ShowItems(preset_type == Preset::TYPE_PRINTER); + sizer_presets->Add(combo_and_btn_sizer, 0, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, margin_5); +#else + wxBOTTOM, 1); + (void)margin_5; // supress unused capture warning +#endif // __WXGTK3__ + } else { + sizer_filaments->Add(combo_and_btn_sizer, 0, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, margin_5); +#else + wxBOTTOM, 1); +#endif // __WXGTK3__ + (*combo)->set_extruder_idx(0); + sizer_filaments->ShowItems(false); + sizer_presets->Add(sizer_filaments, 1, wxEXPAND); + } + }; + + p->combos_filament.push_back(nullptr); + init_combo(&p->combo_print, _L("Print settings"), Preset::TYPE_PRINT, false); + init_combo(&p->combos_filament[0], _L("Filament"), Preset::TYPE_FILAMENT, true); + init_combo(&p->combo_sla_print, _L("SLA print settings"), Preset::TYPE_SLA_PRINT, false); + init_combo(&p->combo_sla_material, _L("SLA material"), Preset::TYPE_SLA_MATERIAL, false); + init_combo(&p->combo_printer, _L("Printer"), Preset::TYPE_PRINTER, false); + + p->sizer_params = new wxBoxSizer(wxVERTICAL); + + // Frequently changed parameters + p->frequently_changed_parameters = new FreqChangedParams(p->scrolled); + p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM +#ifdef __WXGTK3__ + | wxRIGHT +#endif // __WXGTK3__ + , wxOSX ? 1 : margin_5); + + // Object List + p->object_list = new ObjectList(p->scrolled); + p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND); + + // Object Manipulations + p->object_manipulation = new ObjectManipulation(p->scrolled); + p->object_manipulation->Hide(); + p->sizer_params->Add(p->object_manipulation->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); + + // Frequently Object Settings + p->object_settings = new ObjectSettings(p->scrolled); + p->object_settings->Hide(); + p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); + + // Object Layers + p->object_layers = new ObjectLayers(p->scrolled); + p->object_layers->Hide(); + p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); + + // Info boxes + p->object_info = new ObjectInfo(p->scrolled); + p->sliced_info = new SlicedInfo(p->scrolled); + + // Sizer in the scrolled area + if (p->mode_sizer) + scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL); + + int size_margin = wxGTK3 ? wxLEFT | wxRIGHT : wxLEFT; + + is_msw ? + scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | size_margin, margin_5) : + scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | size_margin, margin_5); + scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | size_margin, margin_5); + scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); + scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | size_margin, margin_5); + + // Buttons underneath the scrolled area + + // rescalable bitmap buttons "Send to printer" and "Remove device" + + auto init_scalable_btn = [this](ScalableButton** btn, const std::string& icon_name, wxString tooltip = wxEmptyString) + { +#ifdef __APPLE__ + int bmp_px_cnt = 16; +#else + int bmp_px_cnt = 32; +#endif //__APPLE__ + ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt); + *btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT); + wxGetApp().SetWindowVariantForButton((*btn)); + +#ifdef _WIN32 + (*btn)->Bind(wxEVT_ENTER_WINDOW, [tooltip, btn, this](wxMouseEvent& event) { + p->show_rich_tip(tooltip, *btn); + event.Skip(); + }); + (*btn)->Bind(wxEVT_LEAVE_WINDOW, [btn, this](wxMouseEvent& event) { + p->hide_rich_tip(*btn); + event.Skip(); + }); +#else + (*btn)->SetToolTip(tooltip); +#endif // _WIN32 + (*btn)->Hide(); + }; + + init_scalable_btn(&p->btn_send_gcode , "export_gcode", _L("Send to printer") + " " +GUI::shortkey_ctrl_prefix() + "Shift+G"); +// init_scalable_btn(&p->btn_eject_device, "eject_sd" , _L("Remove device ") + GUI::shortkey_ctrl_prefix() + "T"); + init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _L("Export to SD card / Flash drive") + " " + GUI::shortkey_ctrl_prefix() + "U"); + + // regular buttons "Slice now" and "Export G-code" + +// const int scaled_height = p->btn_eject_device->GetBitmapHeight() + 4; +#ifdef _WIN32 + const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); +#else + const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4; +#endif + auto init_btn = [this](wxButton **btn, wxString label, const int button_height) { + *btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition, + wxSize(-1, button_height), wxBU_EXACTFIT); + wxGetApp().SetWindowVariantForButton((*btn)); + (*btn)->SetFont(wxGetApp().bold_font()); + wxGetApp().UpdateDarkUI((*btn), true); + }; + + init_btn(&p->btn_export_gcode, _L("Export G-code") + dots , scaled_height); + init_btn(&p->btn_reslice , _L("Slice now") , scaled_height); + + enable_buttons(false); + + auto *btns_sizer = new wxBoxSizer(wxVERTICAL); + + auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL); + complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND); + complect_btns_sizer->Add(p->btn_send_gcode, 0, wxLEFT, margin_5); + complect_btns_sizer->Add(p->btn_export_gcode_removable, 0, wxLEFT, margin_5); +// complect_btns_sizer->Add(p->btn_eject_device); + + + btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5); + btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5); + + auto *sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(p->scrolled, 1, wxEXPAND); + sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, margin_5); + SetSizer(sizer); + + // Events + p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); }); + p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) + { + if (p->plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) + return; + + const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT); + if (export_gcode_after_slicing) + p->plater->export_gcode(true); + else + p->plater->reslice(); + p->plater->select_view_3D("Preview"); + }); + +#ifdef _WIN32 + p->btn_reslice->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) { + p->show_rich_tip(p->btn_reslice_tip, p->btn_reslice); + event.Skip(); + }); + p->btn_reslice->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { + p->hide_rich_tip(p->btn_reslice); + event.Skip(); + }); +#endif // _WIN32 + + p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); +// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); }); + p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); }); +} + +Sidebar::~Sidebar() {} + +void Sidebar::init_filament_combo(PlaterPresetComboBox** combo, const int extr_idx) +{ + *combo = new PlaterPresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT); + (*combo)->set_extruder_idx(extr_idx); + + auto combo_and_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + combo_and_btn_sizer->Add(*combo, 1, wxEXPAND); + combo_and_btn_sizer->Add((*combo)->edit_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, + int(0.3*wxGetApp().em_unit())); + + this->p->sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | +#ifdef __WXGTK3__ + wxRIGHT, int(0.5 * wxGetApp().em_unit())); +#else + wxBOTTOM, 1); +#endif // __WXGTK3__ +} + +void Sidebar::remove_unused_filament_combos(const size_t current_extruder_count) +{ + if (current_extruder_count >= p->combos_filament.size()) + return; + auto sizer_filaments = this->p->sizer_filaments; + while (p->combos_filament.size() > current_extruder_count) { + const int last = p->combos_filament.size() - 1; + sizer_filaments->Remove(last); + (*p->combos_filament[last]).Destroy(); + p->combos_filament.pop_back(); + } +} + +void Sidebar::update_all_preset_comboboxes() +{ + PresetBundle &preset_bundle = *wxGetApp().preset_bundle; + const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); + + // Update the print choosers to only contain the compatible presets, update the dirty flags. + if (print_tech == ptFFF) + p->combo_print->update(); + else { + p->combo_sla_print->update(); + p->combo_sla_material->update(); + } + // Update the printer choosers, update the dirty flags. + p->combo_printer->update(); + // Update the filament choosers to only contain the compatible presets, update the color preview, + // update the dirty flags. + if (print_tech == ptFFF) { + for (PlaterPresetComboBox* cb : p->combos_filament) + cb->update(); + } +} + +void Sidebar::update_presets(Preset::Type preset_type) +{ + PresetBundle &preset_bundle = *wxGetApp().preset_bundle; + const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology(); + + switch (preset_type) { + case Preset::TYPE_FILAMENT: + { + const size_t extruder_cnt = print_tech != ptFFF ? 1 : + dynamic_cast(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); + const size_t filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); + + for (size_t i = 0; i < filament_cnt; i++) + p->combos_filament[i]->update(); + + break; + } + + case Preset::TYPE_PRINT: + p->combo_print->update(); + break; + + case Preset::TYPE_SLA_PRINT: + p->combo_sla_print->update(); + break; + + case Preset::TYPE_SLA_MATERIAL: + p->combo_sla_material->update(); + break; + + case Preset::TYPE_PRINTER: + { + update_all_preset_comboboxes(); +#if 1 // #ysFIXME_delete_after_test_of >> it looks like CallAfter() is no need [issue with disapearing of comboboxes are not reproducible] + p->show_preset_comboboxes(); +#else + // CallAfter is really needed here to correct layout of the preset comboboxes, + // when printer technology is changed during a project loading AND/OR switching the application mode. + // Otherwise, some of comboboxes are invisible + CallAfter([this]() { p->show_preset_comboboxes(); }); +#endif + break; + } + + default: break; + } + + // Synchronize config.ini with the current selections. + wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); +} + +void Sidebar::update_mode_sizer() const +{ + if (p->mode_sizer) + p->mode_sizer->SetMode(m_mode); +} + +void Sidebar::change_top_border_for_mode_sizer(bool increase_border) +{ + if (p->mode_sizer) { + p->mode_sizer->set_items_flag(increase_border ? wxTOP : 0); + p->mode_sizer->set_items_border(increase_border ? int(0.5 * wxGetApp().em_unit()) : 0); + } +} + +void Sidebar::update_reslice_btn_tooltip() const +{ + wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]"; + if (m_mode != comSimple) + tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code"); +#ifdef _WIN32 + p->btn_reslice_tip = tooltip; +#else + p->btn_reslice->SetToolTip(tooltip); +#endif +} + +void Sidebar::msw_rescale() +{ + SetMinSize(wxSize(42 * wxGetApp().em_unit(), -1)); + for (PlaterPresetComboBox* combo : std::vector { p->combo_print, + p->combo_sla_print, + p->combo_sla_material, + p->combo_printer } ) + combo->msw_rescale(); + for (PlaterPresetComboBox* combo : p->combos_filament) + combo->msw_rescale(); + + p->frequently_changed_parameters->msw_rescale(); + p->object_list->msw_rescale(); + p->object_manipulation->msw_rescale(); + p->object_layers->msw_rescale(); + +#ifdef _WIN32 + const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); +#else + const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight() + 4; +#endif + p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height)); + p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height)); + + p->scrolled->Layout(); +} + +void Sidebar::sys_color_changed() +{ +#ifdef _WIN32 + wxWindowUpdateLocker noUpdates(this); + + for (wxWindow* win : std::vector{ this, p->sliced_info->GetStaticBox(), p->object_info->GetStaticBox(), p->btn_reslice, p->btn_export_gcode }) + wxGetApp().UpdateDarkUI(win); + for (wxWindow* win : std::vector{ p->scrolled, p->presets_panel }) + wxGetApp().UpdateAllStaticTextDarkUI(win); + for (wxWindow* btn : std::vector{ p->btn_reslice, p->btn_export_gcode }) + wxGetApp().UpdateDarkUI(btn, true); + + if (p->mode_sizer) + p->mode_sizer->sys_color_changed(); + p->frequently_changed_parameters->sys_color_changed(); + p->object_settings->sys_color_changed(); +#endif + + for (PlaterPresetComboBox* combo : std::vector{ p->combo_print, + p->combo_sla_print, + p->combo_sla_material, + p->combo_printer }) + combo->sys_color_changed(); + for (PlaterPresetComboBox* combo : p->combos_filament) + combo->sys_color_changed(); + + p->object_list->sys_color_changed(); + p->object_manipulation->sys_color_changed(); + p->object_layers->sys_color_changed(); + + // btn...->msw_rescale() updates icon on button, so use it + p->btn_send_gcode->sys_color_changed(); +// p->btn_eject_device->msw_rescale(); + p->btn_export_gcode_removable->sys_color_changed(); + + p->scrolled->Layout(); + p->scrolled->Refresh(); +} + +void Sidebar::update_mode_markers() +{ + if (p->mode_sizer) + p->mode_sizer->update_mode_markers(); +} + +ObjectManipulation* Sidebar::obj_manipul() +{ + return p->object_manipulation; +} + +ObjectList* Sidebar::obj_list() +{ + return p->object_list; +} + +ObjectSettings* Sidebar::obj_settings() +{ + return p->object_settings; +} + +ObjectLayers* Sidebar::obj_layers() +{ + return p->object_layers; +} + +wxScrolledWindow* Sidebar::scrolled_panel() +{ + return p->scrolled; +} + +wxPanel* Sidebar::presets_panel() +{ + return p->presets_panel; +} + +ConfigOptionsGroup* Sidebar::og_freq_chng_params(const bool is_fff) +{ + return p->frequently_changed_parameters->get_og(is_fff); +} + +wxButton* Sidebar::get_wiping_dialog_button() +{ + return p->frequently_changed_parameters->get_wiping_dialog_button(); +} + +void Sidebar::update_objects_list_extruder_column(size_t extruders_count) +{ + p->object_list->update_objects_list_extruder_column(extruders_count); +} + +void Sidebar::show_info_sizer() +{ + Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + ModelObjectPtrs objects = p->plater->model().objects; + const int obj_idx = selection.get_object_idx(); + const int inst_idx = selection.get_instance_idx(); + + if (m_mode < comExpert || objects.empty() || obj_idx < 0 || int(objects.size()) <= obj_idx || + inst_idx < 0 || int(objects[obj_idx]->instances.size()) <= inst_idx || + objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed + (selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) || + !(selection.is_single_full_instance() || selection.is_single_volume())) { + p->object_info->Show(false); + return; + } + + const ModelObject* model_object = objects[obj_idx]; + + bool imperial_units = wxGetApp().app_config->get_bool("use_inches"); + double koef = imperial_units ? ObjectManipulation::mm_to_in : 1.0f; + + ModelVolume* vol = nullptr; + Transform3d t; + if (selection.is_single_volume()) { + std::vector obj_idxs, vol_idxs; + wxGetApp().obj_list()->get_selection_indexes(obj_idxs, vol_idxs); + if (vol_idxs.size() != 1) + // Case when this fuction is called between update selection in ObjectList and on Canvas + // Like after try to delete last solid part in object, the object is selected in ObjectLIst when just a part is still selected on Canvas + // see https://github.com/prusa3d/PrusaSlicer/issues/7408 + return; + vol = model_object->volumes[vol_idxs[0]]; + t = model_object->instances[inst_idx]->get_matrix() * vol->get_matrix(); + } + + Vec3d size = vol ? vol->mesh().transformed_bounding_box(t).size() : model_object->instance_bounding_box(inst_idx).size(); + p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f", size(0)*koef, size(1)*koef, size(2)*koef)); +// p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); + + const TriangleMeshStats& stats = vol ? vol->mesh().stats() : model_object->get_object_stl_stats(); + + double volume_val = stats.volume; + if (vol) + volume_val *= std::fabs(t.matrix().block(0, 0, 3, 3).determinant()); + + p->object_info->info_volume->SetLabel(wxString::Format("%.2f", volume_val * pow(koef,3))); + p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts), + static_cast(model_object->facets_count()), stats.number_of_parts)); + + wxString info_manifold_label; + auto mesh_errors = obj_list()->get_mesh_errors_info(&info_manifold_label); + wxString tooltip = mesh_errors.tooltip; + p->object_info->update_warning_icon(mesh_errors.warning_icon_name); + p->object_info->info_manifold->SetLabel(info_manifold_label); + p->object_info->info_manifold->SetToolTip(tooltip); + p->object_info->manifold_warning_icon->SetToolTip(tooltip); + + p->object_info->show_sizer(true); + if (vol || model_object->volumes.size() == 1) + p->object_info->info_icon->Hide(); + + if (p->plater->printer_technology() == ptSLA) { + for (auto item: p->object_info->sla_hidden_items) + item->Show(false); + } +} + +void Sidebar::update_sliced_info_sizer() +{ + if (p->sliced_info->IsShown(size_t(0))) + { + if (p->plater->printer_technology() == ptSLA) + { + const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics(); + wxString new_label = _L("Used Material (ml)") + ":"; + const bool is_supports = ps.support_used_material > 0.0; + if (is_supports) + new_label += format_wxstr("\n - %s\n - %s", _L_PLURAL("object", "objects", p->plater->model().objects.size()), _L("supports and pad")); + + wxString info_text = is_supports ? + wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000, + ps.objects_used_material / 1000, + ps.support_used_material / 1000) : + wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000); + p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label); + + wxString str_total_cost = "N/A"; + + DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_SLA_MATERIAL)->get_config(); + if (cfg->option("bottle_cost")->getFloat() > 0.0 && + cfg->option("bottle_volume")->getFloat() > 0.0) + { + double material_cost = cfg->option("bottle_cost")->getFloat() / + cfg->option("bottle_volume")->getFloat(); + str_total_cost = wxString::Format("%.3f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); + } + p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost"); + + wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : from_u8(short_time_ui(get_time_dhms(float(ps.estimated_print_time)))); + p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _L("Estimated printing time") + ":"); + + p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + boost::nowide::narrow(t_est), p->plater->is_sidebar_collapsed()); + + // Hide non-SLA sliced info parameters + p->sliced_info->SetTextAndShow(siFilament_m, "N/A"); + p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A"); + p->sliced_info->SetTextAndShow(siFilament_g, "N/A"); + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, "N/A"); + } + else + { + const PrintStatistics& ps = p->plater->fff_print().print_statistics(); + const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; + + bool imperial_units = wxGetApp().app_config->get_bool("use_inches"); + double koef = imperial_units ? ObjectManipulation::in_to_mm : 1000.0; + + wxString new_label = imperial_units ? _L("Used Filament (in)") : _L("Used Filament (m)"); + if (is_wipe_tower) + new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower")); + + wxString info_text = is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_used_filament / koef, + (ps.total_used_filament - ps.total_wipe_tower_filament) / koef, + ps.total_wipe_tower_filament / koef) : + wxString::Format("%.2f", ps.total_used_filament / koef); + p->sliced_info->SetTextAndShow(siFilament_m, info_text, new_label); + + koef = imperial_units ? pow(ObjectManipulation::mm_to_in, 3) : 1.0f; + new_label = imperial_units ? _L("Used Filament (in³)") : _L("Used Filament (mm³)"); + info_text = wxString::Format("%.2f", imperial_units ? ps.total_extruded_volume * koef : ps.total_extruded_volume); + p->sliced_info->SetTextAndShow(siFilament_mm3, info_text, new_label); + + if (ps.total_weight == 0.0) + p->sliced_info->SetTextAndShow(siFilament_g, "N/A"); + else { + new_label = _L("Used Filament (g)"); + info_text = wxString::Format("%.2f", ps.total_weight); + + if (ps.filament_stats.size() > 1) + new_label += ":"; + + const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; + for (const auto& [filament_id, filament_vol] : ps.filament_stats) { + assert(filament_id < extruders_filaments.size()); + if (const Preset* preset = extruders_filaments[filament_id].get_selected_preset()) { + double filament_weight; + if (ps.filament_stats.size() == 1) + filament_weight = ps.total_weight; + else { + double filament_density = preset->config.opt_float("filament_density", 0); + filament_weight = filament_vol * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter; + + new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament_id + 1); + info_text += wxString::Format("\n%.2f", filament_weight); + } + + double spool_weight = preset->config.opt_float("filament_spool_weight", 0); + if (spool_weight != 0.0) { + new_label += "\n " + _L("(including spool)"); + info_text += wxString::Format(" (%.2f)\n", filament_weight + spool_weight); + } + } + } + + p->sliced_info->SetTextAndShow(siFilament_g, info_text, new_label); + } + + new_label = _L("Cost"); + if (is_wipe_tower) + new_label += format_wxstr(":\n - %1%\n - %2%", _L("objects"), _L("wipe tower")); + + info_text = ps.total_cost == 0.0 ? "N/A" : + is_wipe_tower ? + wxString::Format("%.2f \n%.2f \n%.2f", ps.total_cost, + (ps.total_cost - ps.total_wipe_tower_cost), + ps.total_wipe_tower_cost) : + wxString::Format("%.2f", ps.total_cost); + p->sliced_info->SetTextAndShow(siCost, info_text, new_label); + + if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A") + p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A"); + else { + info_text = ""; + new_label = _L("Estimated printing time") + ":"; + if (ps.estimated_normal_print_time != "N/A") { + new_label += format_wxstr("\n - %1%", _L("normal mode")); + info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_normal_print_time)); + + p->plater->get_notification_manager()->set_slicing_complete_print_time(_u8L("Estimated printing time") + ": " + ps.estimated_normal_print_time, p->plater->is_sidebar_collapsed()); + + } + if (ps.estimated_silent_print_time != "N/A") { + new_label += format_wxstr("\n - %1%", _L("stealth mode")); + info_text += format_wxstr("\n%1%", short_time_ui(ps.estimated_silent_print_time)); + } + p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label); + } + + // if there is a wipe tower, insert number of toolchanges info into the array: + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", ps.total_toolchanges) : "N/A"); + + // Hide non-FFF sliced info parameters + p->sliced_info->SetTextAndShow(siMateril_unit, "N/A"); + } + } + + Layout(); +} + +void Sidebar::show_sliced_info_sizer(const bool show) +{ + wxWindowUpdateLocker freeze_guard(this); + + p->sliced_info->Show(show); + if (show) + update_sliced_info_sizer(); + + Layout(); + p->scrolled->Refresh(); +} + +void Sidebar::enable_buttons(bool enable) +{ + p->btn_reslice->Enable(enable); + p->btn_export_gcode->Enable(enable); + p->btn_send_gcode->Enable(enable); +// p->btn_eject_device->Enable(enable); + p->btn_export_gcode_removable->Enable(enable); +} + +bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); } +bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); } +bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); } +bool Sidebar::show_export_removable(bool show) const { return p->btn_export_gcode_removable->Show(show); } +//bool Sidebar::show_eject(bool show) const { return p->btn_eject_device->Show(show); } +//bool Sidebar::get_eject_shown() const { return p->btn_eject_device->IsShown(); } + +bool Sidebar::is_multifilament() +{ + return p->combos_filament.size() > 1; +} + +void Sidebar::update_mode() +{ + m_mode = wxGetApp().get_mode(); + + update_reslice_btn_tooltip(); + update_mode_sizer(); + + wxWindowUpdateLocker noUpdates(this); + + if (m_mode == comSimple) + p->object_manipulation->set_coordinates_type(ECoordinatesType::World); + + p->object_list->get_sizer()->Show(m_mode > comSimple); + + p->object_list->unselect_objects(); + p->object_list->update_selections(); + + Layout(); +} + +void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const +{ + switch (btn_type) + { + case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break; + case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break; + case ActionButtonType::abSendGCode: /*p->btn_send_gcode->SetLabelText(label);*/ break; + } +} + +bool Sidebar::is_collapsed() { return p->is_collapsed; } + +void Sidebar::collapse(bool collapse) +{ + p->is_collapsed = collapse; + + this->Show(!collapse); + p->plater->Layout(); + + // save collapsing state to the AppConfig + if (wxGetApp().is_editor()) + wxGetApp().app_config->set("collapsed_sidebar", collapse ? "1" : "0"); +} + +#ifdef _MSW_DARK_MODE +void Sidebar::show_mode_sizer(bool show) +{ + p->mode_sizer->Show(show); +} +#endif + +void Sidebar::update_ui_from_settings() +{ + p->object_manipulation->update_ui_from_settings(); + show_info_sizer(); + update_sliced_info_sizer(); + // update Cut gizmo, if it's open + p->plater->canvas3D()->update_gizmos_on_off_state(); + p->plater->set_current_canvas_as_dirty(); + p->plater->get_current_canvas3D()->request_extra_frame(); + p->object_list->apply_volumes_order(); +} + +std::vector& Sidebar::combos_filament() +{ + return p->combos_filament; +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Sidebar.hpp b/src/slic3r/GUI/Sidebar.hpp new file mode 100644 index 0000000000..b904cf0184 --- /dev/null +++ b/src/slic3r/GUI/Sidebar.hpp @@ -0,0 +1,115 @@ +///|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ +///|/ ported from lib/Slic3r/GUI/Plater.pm: +///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2018 Martin Loidl @LoidlM +///|/ Copyright (c) 2017 Matthias Gazzari @qtux +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens +///|/ Copyright (c) 2015 Daren Schwenke +///|/ Copyright (c) 2014 Mark Hindess +///|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Sam Wong +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ +#ifndef slic3r_Sidebar_hpp_ +#define slic3r_Sidebar_hpp_ + +#include + +#include + +#include "libslic3r/Preset.hpp" +#include "GUI.hpp" +#include "Event.hpp" + +class wxButton; +class wxScrolledWindow; +class wxString; + +namespace Slic3r { + +namespace GUI { + +wxDECLARE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); + +class ConfigOptionsGroup; +class ObjectManipulation; +class ObjectSettings; +class ObjectLayers; +class ObjectList; +class PlaterPresetComboBox; +class Plater; + +enum class ActionButtonType : int { + abReslice, + abExport, + abSendGCode +}; + +class Sidebar : public wxPanel +{ + ConfigOptionMode m_mode{ConfigOptionMode::comSimple}; +public: + Sidebar(Plater *parent); + Sidebar(Sidebar &&) = delete; + Sidebar(const Sidebar &) = delete; + Sidebar &operator=(Sidebar &&) = delete; + Sidebar &operator=(const Sidebar &) = delete; + ~Sidebar(); + + void init_filament_combo(PlaterPresetComboBox **combo, const int extr_idx); + void remove_unused_filament_combos(const size_t current_extruder_count); + void update_all_preset_comboboxes(); + void update_presets(Preset::Type preset_type); + void update_mode_sizer() const; + void change_top_border_for_mode_sizer(bool increase_border); + void update_reslice_btn_tooltip() const; + void msw_rescale(); + void sys_color_changed(); + void update_mode_markers(); + + ObjectManipulation* obj_manipul(); + ObjectList* obj_list(); + ObjectSettings* obj_settings(); + ObjectLayers* obj_layers(); + wxScrolledWindow* scrolled_panel(); + wxPanel* presets_panel(); + + ConfigOptionsGroup* og_freq_chng_params(const bool is_fff); + wxButton* get_wiping_dialog_button(); + void update_objects_list_extruder_column(size_t extruders_count); + void show_info_sizer(); + void show_sliced_info_sizer(const bool show); + void update_sliced_info_sizer(); + void enable_buttons(bool enable); + void set_btn_label(const ActionButtonType btn_type, const wxString& label) const; + bool show_reslice(bool show) const; + bool show_export(bool show) const; + bool show_send(bool show) const; + bool show_eject(bool show)const; + bool show_export_removable(bool show) const; + bool get_eject_shown() const; + bool is_multifilament(); + void update_mode(); + bool is_collapsed(); + void collapse(bool collapse); + void update_ui_from_settings(); + +#ifdef _MSW_DARK_MODE + void show_mode_sizer(bool show); +#endif + + std::vector& combos_filament(); + +private: + struct priv; + std::unique_ptr p; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif