mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 07:41:06 +00:00
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes. clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer. * Remove Unused Project Includes From Files With Platform-Specific Code A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block. * Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass The platform-file pass treated pchheader.hpp as an ordinary header and emptied it, and left GUI_Preview.hpp and 14 other files relying on headers they no longer reached directly. * Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call * Include Headers That Files Reached Through Ones the Cleanup Removed * Drop Includes Duplicated by the Cleanup or by Main's Own Additions * Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
1459 lines
55 KiB
C++
1459 lines
55 KiB
C++
#include "OptionsGroup.hpp"
|
|
#include "Plater.hpp"
|
|
#include "SettingsIndex.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "MainFrame.hpp"
|
|
#include "OG_CustomCtrl.hpp"
|
|
#include "MsgDialog.hpp"
|
|
#include "PluginPickerDialog.hpp"
|
|
#include "Widgets/StaticLine.hpp"
|
|
#include "Widgets/LabeledStaticBox.hpp"
|
|
|
|
#include <boost/any.hpp>
|
|
#include <boost/log/trivial.hpp>
|
|
#include <functional>
|
|
#include <cstddef>
|
|
#include <iostream>
|
|
#include <libslic3r/Config.hpp>
|
|
#include "slic3r/GUI/Field.hpp"
|
|
#include "libslic3r/Preset.hpp"
|
|
#include "slic3r/GUI/Widgets/Label.hpp"
|
|
#include "slic3r/plugin/PluginDescriptor.hpp"
|
|
#include "slic3r/GUI/GUI.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <wx/bookctrl.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/event.h>
|
|
#include <wx/numformatter.h>
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include "libslic3r/Exception.hpp"
|
|
#include "slic3r/plugin/PluginManager.hpp"
|
|
#include "I18N.hpp"
|
|
#include <algorithm>
|
|
#include <locale>
|
|
#include <wx/sizer.h>
|
|
#include <wx/stattext.h>
|
|
#include <wx/utils.h>
|
|
#include "libslic3r/PresetBundle.hpp"
|
|
#include "slic3r/GUI/wxExtensions.hpp"
|
|
|
|
namespace Slic3r { namespace GUI {
|
|
|
|
// BBS: new layout
|
|
constexpr int titleWidth = 20;
|
|
|
|
const t_field& OptionsGroup::build_field(const Option& opt) { return build_field(opt.opt_id, opt.opt); }
|
|
const t_field& OptionsGroup::build_field(const t_config_option_key& id)
|
|
{
|
|
const ConfigOptionDef& opt = m_options.at(id).opt;
|
|
return build_field(id, opt);
|
|
}
|
|
|
|
const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt)
|
|
{
|
|
// Check the gui_type field first, fall through
|
|
// is the normal type.
|
|
switch (opt.gui_type) {
|
|
case ConfigOptionDef::GUIType::select_open: m_fields.emplace(id, Choice::Create<Choice>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::color: m_fields.emplace(id, ColourPicker::Create<ColourPicker>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::f_enum_open:
|
|
case ConfigOptionDef::GUIType::i_enum_open: m_fields.emplace(id, Choice::Create<Choice>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::slider: m_fields.emplace(id, SliderCtrl::Create<SliderCtrl>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::legend: // StaticText
|
|
m_fields.emplace(id, StaticText::Create<StaticText>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case ConfigOptionDef::GUIType::one_string: m_fields.emplace(id, TextCtrl::Create<TextCtrl>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::plugin_picker: m_fields.emplace(id, PluginField::Create<PluginField>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::plugin_config: m_fields.emplace(id, PluginConfigField::Create<PluginConfigField>(this->ctrl_parent(), opt, id)); break;
|
|
case ConfigOptionDef::GUIType::printer_agent_select: m_fields.emplace(
|
|
id, PrinterAgentChoice::Create<PrinterAgentChoice>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
default:
|
|
switch (opt.type) {
|
|
case coFloatOrPercent:
|
|
case coFloatsOrPercents:
|
|
case coFloat:
|
|
case coFloats:
|
|
case coPercent:
|
|
case coPercents:
|
|
case coString:
|
|
case coStrings:
|
|
m_fields.emplace(id, TextCtrl::Create<TextCtrl>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case coBool:
|
|
case coBools:
|
|
m_fields.emplace(id, CheckBox::Create<CheckBox>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case coInt:
|
|
case coInts:
|
|
m_fields.emplace(id, SpinCtrl::Create<SpinCtrl>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case coEnum:
|
|
case coEnums:
|
|
m_fields.emplace(id, Choice::Create<Choice>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case coPoint:
|
|
case coPoints:
|
|
m_fields.emplace(id, PointCtrl::Create<PointCtrl>(this->ctrl_parent(), opt, id));
|
|
break;
|
|
case coNone: break;
|
|
default:
|
|
throw Slic3r::LogicError("This control doesn't exist till now"); break;
|
|
}
|
|
}
|
|
// Grab a reference to fields for convenience
|
|
const t_field& field = m_fields[id];
|
|
field->m_on_change = [this](const std::string& opt_id, const boost::any& value) {
|
|
//! This function will be called from Field.
|
|
//! Call OptionGroup._on_change(...)
|
|
if (!m_disabled)
|
|
this->on_change_OG(opt_id, value);
|
|
};
|
|
field->m_on_kill_focus = [this](const std::string& opt_id) {
|
|
//! This function will be called from Field.
|
|
if (!m_disabled)
|
|
this->on_kill_focus(opt_id);
|
|
};
|
|
field->m_parent = parent();
|
|
|
|
if (edit_custom_gcode && opt.is_code) {
|
|
field->m_fn_edit_value = [this](std::string opt_id) {
|
|
if (!m_disabled)
|
|
this->edit_custom_gcode(opt_id);
|
|
};
|
|
field->set_edit_tooltip(_L("Edit Custom G-code"));
|
|
}
|
|
|
|
field->m_back_to_initial_value = [this](std::string opt_id) {
|
|
if (!m_disabled)
|
|
this->back_to_initial_value(opt_id);
|
|
};
|
|
field->m_back_to_sys_value = [this](std::string opt_id) {
|
|
if (!this->m_disabled)
|
|
this->back_to_sys_value(opt_id);
|
|
};
|
|
|
|
if (auto plugin_field = dynamic_cast<PluginField*>(field.get())) {
|
|
plugin_field->set_selector([this, plugin_field]() -> std::string {
|
|
if (m_disabled)
|
|
return {};
|
|
return this->pick_plugin(plugin_field->m_opt);
|
|
});
|
|
}
|
|
|
|
// The dialog behind the button edits one preset's overrides, so it has to know which preset. Set
|
|
// here because fields are built lazily on activate() — too late for the Tab to reach in afterwards.
|
|
if (auto plugin_config_field = dynamic_cast<PluginConfigField*>(field.get()))
|
|
if (auto config_group = dynamic_cast<ConfigOptionsGroup*>(this))
|
|
plugin_config_field->set_preset_type(config_group->config_type());
|
|
|
|
// assign function objects for callbacks, etc.
|
|
return field;
|
|
}
|
|
|
|
OptionsGroup::OptionsGroup(
|
|
wxWindow* _parent, const wxString& title, const wxString& icon, bool is_tab_opt /* = false */, column_t extra_clmn /* = nullptr */)
|
|
: m_parent(_parent)
|
|
, title(title)
|
|
, icon(icon)
|
|
, m_use_custom_ctrl(is_tab_opt)
|
|
,
|
|
// BBS: new layout
|
|
staticbox(!is_tab_opt)
|
|
, extra_column(extra_clmn)
|
|
{}
|
|
|
|
wxWindow* OptionsGroup::ctrl_parent() const
|
|
{
|
|
// BBS: new layout
|
|
return this->custom_ctrl && m_use_custom_ctrl_as_parent ? static_cast<wxWindow*>(this->custom_ctrl) :
|
|
(staticbox ? static_cast<wxWindow*>(this->stb) : this->parent());
|
|
}
|
|
|
|
bool OptionsGroup::is_legend_line()
|
|
{
|
|
if (m_lines.size() == 1) {
|
|
const std::vector<Option>& option_set = m_lines.front().get_options();
|
|
return !option_set.empty() && option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OptionsGroup::set_max_win_width(int max_win_width)
|
|
{
|
|
if (custom_ctrl)
|
|
custom_ctrl->set_max_win_width(max_win_width);
|
|
}
|
|
|
|
void OptionsGroup::remove_option_if(std::function<bool(std::string const&)> const& comp)
|
|
{
|
|
for (auto& l : m_lines) {
|
|
auto& opts = const_cast<std::vector<Option>&>(l.get_options());
|
|
opts.erase(std::remove_if(opts.begin(), opts.end(), [&comp](Option& o) { return comp(o.opt.opt_key); }), opts.end());
|
|
l.undo_to_sys = true;
|
|
}
|
|
for (int i = m_lines.size() - 1; i >= 0; --i) {
|
|
if (m_lines[i].get_options().empty())
|
|
m_options_mode.erase(m_options_mode.begin() + i);
|
|
}
|
|
m_lines.erase(std::remove_if(m_lines.begin(), m_lines.end(), [](auto& l) { return l.get_options().empty(); }), m_lines.end());
|
|
// TODO: remove items from g->m_options;
|
|
}
|
|
|
|
void OptionsGroup::show_field(const t_config_option_key& opt_key, bool show /* = true*/)
|
|
{
|
|
Field* field = get_field(opt_key);
|
|
if (!field)
|
|
return;
|
|
wxWindow* win = field->getWindow();
|
|
if (!win)
|
|
return;
|
|
wxSizerItem* win_item = m_grid_sizer->GetItem(win, true);
|
|
if (!win_item)
|
|
return;
|
|
|
|
const size_t cols = (size_t) m_grid_sizer->GetCols();
|
|
const size_t rows = (size_t) m_grid_sizer->GetEffectiveRowsCount();
|
|
|
|
auto show_row = [this, show, cols, win_item](wxSizerItem* item, size_t row_shift) {
|
|
// check if item contanes required win
|
|
if (!item->IsWindow() || item != win_item)
|
|
return false;
|
|
// show/hide hole line contanes this window
|
|
for (size_t i = 0; i < cols; ++i)
|
|
m_grid_sizer->Show(row_shift + i, show);
|
|
return true;
|
|
};
|
|
|
|
size_t row_shift = 0;
|
|
for (size_t j = 0; j < rows; ++j) {
|
|
for (size_t i = 0; i < cols; ++i) {
|
|
wxSizerItem* item = m_grid_sizer->GetItem(row_shift + i);
|
|
if (!item)
|
|
continue;
|
|
if (item->IsSizer()) {
|
|
for (wxSizerItem* child_item : item->GetSizer()->GetChildren())
|
|
if (show_row(child_item, row_shift))
|
|
return;
|
|
} else if (show_row(item, row_shift))
|
|
return;
|
|
}
|
|
row_shift += cols;
|
|
}
|
|
}
|
|
|
|
void OptionsGroup::enable_field(const t_config_option_key& opt_key, bool enable)
|
|
{
|
|
if (Field* f = get_field(opt_key); f) {
|
|
f->toggle(enable);
|
|
}
|
|
}
|
|
|
|
void OptionsGroup::set_name(const wxString& new_name) { stb->SetLabel(new_name); }
|
|
|
|
void OptionsGroup::append_line(const Line& line)
|
|
{
|
|
m_lines.emplace_back(line);
|
|
|
|
// Feed the searcher the row's wiki path (Line::label_path, for the Speed Dial's "open wiki"
|
|
// affordance) and the label the row actually draws, so a setting action is named like the page.
|
|
if (m_use_custom_ctrl) {
|
|
Search::SettingsIndex& index = wxGetApp().sidebar().settings_index();
|
|
const Preset::Type type = static_cast<Preset::Type>(config_type());
|
|
const bool multi = line.get_options().size() > 1;
|
|
for (const auto& opt : line.get_options()) {
|
|
if (!line.label_path.empty())
|
|
index.set_path(opt.opt_id, type, line.label_path);
|
|
// Mirror the sub-label OG_CustomCtrl draws for a multi-option row, so the palette
|
|
// names each field like the page does.
|
|
const std::string& leaf_src = opt.opt.label;
|
|
const wxString leaf = (leaf_src == L_CONTEXT("Top", "Layers") || leaf_src == L_CONTEXT("Bottom", "Layers")) ?
|
|
_L_CONTEXT(leaf_src, "Layers") :
|
|
_(leaf_src);
|
|
index.set_line_label(opt.opt_id, type, Search::compose_display_label(line.label, leaf, multi));
|
|
}
|
|
}
|
|
|
|
if (line.full_width && (line.widget != nullptr || !line.get_extra_widgets().empty()))
|
|
return;
|
|
|
|
auto option_set = line.get_options();
|
|
for (auto opt : option_set)
|
|
m_options.emplace(opt.opt_id, opt);
|
|
|
|
// add mode value for current line to m_options_mode
|
|
if (!option_set.empty())
|
|
m_options_mode.push_back(option_set[0].opt.mode);
|
|
}
|
|
|
|
// BBS: get line for opt_key
|
|
Line* OptionsGroup::get_line(const std::string& opt_key)
|
|
{
|
|
for (int index = 0; index < m_lines.size(); index++) {
|
|
for (auto& opt : m_lines[index].get_options())
|
|
if (opt.opt_id == opt_key)
|
|
return &(m_lines[index]);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void OptionsGroup::append_separator() { m_lines.emplace_back(Line()); }
|
|
|
|
void OptionsGroup::activate_line(Line& line)
|
|
{
|
|
if (line.is_separator())
|
|
return;
|
|
|
|
m_use_custom_ctrl_as_parent = false;
|
|
|
|
if (line.full_width && (line.widget != nullptr || !line.get_extra_widgets().empty())) {
|
|
// BBS: new layout
|
|
const auto h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
sizer->Add(h_sizer, 1, wxEXPAND | wxALL, (wxOSX && !staticbox) ? 0 : 15);
|
|
if (line.widget != nullptr) {
|
|
// description lines
|
|
sizer->Add(line.widget(this->ctrl_parent()), 0, wxEXPAND | wxALL, (wxOSX && !staticbox) ? 0 : 15);
|
|
return;
|
|
}
|
|
if (!line.get_extra_widgets().empty()) {
|
|
bool is_first_item = true;
|
|
for (auto extra_widget : line.get_extra_widgets()) {
|
|
h_sizer->Add(extra_widget(this->ctrl_parent()), is_first_item ? 1 : 0, wxLEFT, titleWidth * wxGetApp().em_unit());
|
|
is_first_item = false;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto option_set = line.get_options();
|
|
bool is_legend_line = option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend;
|
|
|
|
if (!custom_ctrl && m_use_custom_ctrl) {
|
|
custom_ctrl = new OG_CustomCtrl(is_legend_line || !staticbox ? this->parent() : static_cast<wxWindow*>(this->stb), this);
|
|
// BBS: new layout
|
|
custom_ctrl->SetLabel("");
|
|
if (is_legend_line)
|
|
sizer->Add(custom_ctrl, 0, wxEXPAND | wxLEFT, (wxOSX && !staticbox) ? 0 : 10);
|
|
else
|
|
sizer->Add(custom_ctrl, 0, wxEXPAND | wxALL, !staticbox ? 0 : 5);
|
|
}
|
|
|
|
// Set sidetext width for a better alignment of options in line
|
|
// "m_show_modified_btns==true" means that options groups are in tabs
|
|
if (option_set.size() > 1 && m_use_custom_ctrl) {
|
|
sublabel_width = Field::def_width() + 1;
|
|
sidetext_width = Field::def_width_thinner();
|
|
}
|
|
|
|
// if we have a single option with no label, no sidetext just add it directly to sizer
|
|
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && option_set.front().opt.sidetext.size() == 0 &&
|
|
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
|
|
const auto& option = option_set.front();
|
|
const auto& field = build_field(option);
|
|
|
|
// BBS: new layout
|
|
const auto h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
sizer->Add(h_sizer, 1, wxEXPAND | wxALL, (wxOSX && !staticbox) ? 0 : 5);
|
|
|
|
int left_padding = option.opt.multiline ? 0 : titleWidth * wxGetApp().em_unit();
|
|
if (m_labels_hidden)
|
|
left_padding = 0;
|
|
|
|
if (is_window_field(field)) {
|
|
int flags = wxEXPAND;
|
|
if (left_padding > 0)
|
|
flags |= wxLEFT;
|
|
h_sizer->Add(field->getWindow(), 1, flags, left_padding);
|
|
}
|
|
if (is_sizer_field(field)) {
|
|
int flags = wxEXPAND;
|
|
if (left_padding > 0)
|
|
flags |= wxLEFT;
|
|
h_sizer->Add(field->getSizer(), 1, flags, left_padding);
|
|
}
|
|
return;
|
|
}
|
|
|
|
auto grid_sizer = m_grid_sizer;
|
|
|
|
if (custom_ctrl)
|
|
m_use_custom_ctrl_as_parent = true;
|
|
|
|
// if we have an extra column, build it
|
|
if (extra_column) {
|
|
m_extra_column_item_ptrs.push_back(extra_column(this->ctrl_parent(), line));
|
|
grid_sizer->Add(m_extra_column_item_ptrs.back(), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 3);
|
|
}
|
|
|
|
// Build a label if we have it
|
|
wxStaticText* label = nullptr;
|
|
if (label_width != 0) {
|
|
if (custom_ctrl) {
|
|
if (line.near_label_widget)
|
|
line.near_label_widget_win = line.near_label_widget(this->ctrl_parent());
|
|
} else {
|
|
if (!line.near_label_widget || !line.label.IsEmpty()) {
|
|
// Only create the label if it is going to be displayed.
|
|
long label_style = staticbox ? 0 : wxALIGN_RIGHT;
|
|
#ifdef __WXGTK__
|
|
// workaround for correct text align of the StaticBox on Linux
|
|
// flags wxALIGN_RIGHT and wxALIGN_CENTRE don't work when Ellipsize flags are _not_ given.
|
|
// Text is properly aligned only when Ellipsize is checked.
|
|
label_style |= staticbox ? 0 : wxST_ELLIPSIZE_END;
|
|
#endif /* __WXGTK__ */
|
|
label = new wxStaticText(this->ctrl_parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "), wxDefaultPosition,
|
|
wxSize(label_width * wxGetApp().em_unit(), -1), label_style);
|
|
label->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
label->SetFont(wxGetApp().normal_font());
|
|
label->Wrap(label_width * wxGetApp().em_unit()); // avoid a Linux/GTK bug
|
|
}
|
|
if (!line.near_label_widget)
|
|
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, line.label.IsEmpty() ? 0 : 5);
|
|
else if (!line.label.IsEmpty()) {
|
|
// If we're here, we have some widget near the label
|
|
// so we need a horizontal sizer to arrange these things
|
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
|
|
sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
|
|
}
|
|
if (label != nullptr && line.label_tooltip != "")
|
|
label->SetToolTip(line.label_tooltip);
|
|
line.label_widget = label;
|
|
}
|
|
}
|
|
|
|
// If there's a widget, build it and add the result to the sizer.
|
|
if (line.widget != nullptr) {
|
|
auto wgt = line.widget(this->ctrl_parent());
|
|
if (custom_ctrl)
|
|
line.widget_sizer = wgt;
|
|
else
|
|
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5);
|
|
return;
|
|
}
|
|
|
|
// If we're here, we have more than one option or a single option with sidetext
|
|
// so we need a horizontal sizer to arrange these things
|
|
auto sizer = custom_ctrl ? nullptr : new wxBoxSizer(wxHORIZONTAL);
|
|
if (!custom_ctrl)
|
|
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
|
|
// If we have a single option with no sidetext just add it directly to the grid sizer
|
|
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
|
line.get_extra_widgets().size() == 0) {
|
|
const auto& option = option_set.front();
|
|
const auto& field = build_field(option);
|
|
|
|
if (!custom_ctrl) {
|
|
int flags = option.opt.full_width ? wxEXPAND : wxALIGN_CENTER_VERTICAL;
|
|
if (m_labels_hidden)
|
|
flags &= ~wxALIGN_CENTER_VERTICAL;
|
|
int border = (wxOSX || !staticbox) ? 0 : 2;
|
|
|
|
if (is_window_field(field))
|
|
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, wxBOTTOM | wxTOP | flags, border);
|
|
if (is_sizer_field(field))
|
|
sizer->Add(field->getSizer(), 1, flags, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool is_multioption_line = option_set.size() > 1;
|
|
for (auto opt : option_set) {
|
|
ConfigOptionDef option = opt.opt;
|
|
wxSizer* sizer_tmp = sizer;
|
|
// add label if any
|
|
if ((is_multioption_line || line.label.IsEmpty()) && !option.label.empty() && !custom_ctrl) {
|
|
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
|
wxString str_label = (option.label == L_CONTEXT("Top", "Layers") || option.label == L_CONTEXT("Bottom", "Layers")) ?
|
|
_L_CONTEXT(option.label, "Layers") :
|
|
_(option.label);
|
|
label = new wxStaticText(this->ctrl_parent(), wxID_ANY, str_label + ": ", wxDefaultPosition, // wxDefaultSize);
|
|
wxSize(sublabel_width != -1 ? sublabel_width * wxGetApp().em_unit() : -1, -1), wxALIGN_RIGHT);
|
|
label->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
label->SetFont(wxGetApp().normal_font());
|
|
sizer_tmp->Add(label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
|
}
|
|
|
|
// add field
|
|
const Option& opt_ref = opt;
|
|
auto& field = build_field(opt_ref);
|
|
if (!custom_ctrl) {
|
|
if (option_set.size() == 1 && option_set.front().opt.full_width) {
|
|
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
|
|
sizer_tmp->Add(v_sizer, 1, wxEXPAND);
|
|
is_sizer_field(field) ? v_sizer->Add(field->getSizer(), 0, wxEXPAND) : v_sizer->Add(field->getWindow(), 0, wxEXPAND);
|
|
break;
|
|
}
|
|
|
|
is_sizer_field(field) ? sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
|
|
sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
|
|
|
|
// add sidetext if any
|
|
if (!field->combine_side_text() && (!option.sidetext.empty() || sidetext_width > 0)) {
|
|
auto sidetext = new wxStaticText(this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
|
|
wxSize(sidetext_width != -1 ? sidetext_width * wxGetApp().em_unit() : -1, -1),
|
|
wxALIGN_LEFT);
|
|
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
|
sidetext->SetFont(wxGetApp().normal_font());
|
|
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
|
}
|
|
|
|
// add side widget if any
|
|
if (opt.side_widget != nullptr) {
|
|
sizer_tmp->Add(opt.side_widget(this->ctrl_parent()) /*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL,
|
|
1); //! requires verification
|
|
}
|
|
|
|
if (opt.opt_id != option_set.back().opt_id) //! istead of (opt != option_set.back())
|
|
sizer_tmp->AddSpacer(6);
|
|
}
|
|
}
|
|
|
|
// add extra sizers if any
|
|
for (auto extra_widget : line.get_extra_widgets()) {
|
|
if (line.get_extra_widgets().size() == 1 && !staticbox) {
|
|
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
|
|
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
|
|
sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND);
|
|
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
|
|
return;
|
|
}
|
|
|
|
line.extra_widget_sizer = extra_widget(this->ctrl_parent());
|
|
if (!custom_ctrl)
|
|
sizer->Add(line.extra_widget_sizer, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
|
|
}
|
|
}
|
|
|
|
// create all controls for the option group from the m_lines
|
|
bool OptionsGroup::activate(std::function<void()> throw_if_canceled /* = [](){}*/, int horiz_alignment /* = wxALIGN_LEFT*/)
|
|
{
|
|
if (sizer) //(!sizer->IsEmpty())
|
|
return false;
|
|
|
|
try {
|
|
if (staticbox) {
|
|
// ORCA match style of wxStaticBox between platforms
|
|
LabeledStaticBox* stb = new LabeledStaticBox(m_parent, _(title));
|
|
// wxGetApp().UpdateDarkUI(stb);
|
|
this->stb = stb;
|
|
sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
|
|
} else {
|
|
// BBS: new layout
|
|
::StaticLine* stl = new ::StaticLine(m_parent, false, _(title), icon);
|
|
stl->SetFont(Label::Head_14);
|
|
stl->SetForegroundColour("#363636"); // ORCA Match Parameters title color with tab title color
|
|
sizer = new wxBoxSizer(wxVERTICAL);
|
|
if (title.IsEmpty()) {
|
|
stl->Hide();
|
|
} else {
|
|
sizer->Add(stl, 0, wxEXPAND);
|
|
sizer->AddSpacer(8);
|
|
}
|
|
this->stb = stl;
|
|
}
|
|
|
|
auto num_columns = 1U;
|
|
size_t grow_col = 1;
|
|
|
|
if (label_width == 0)
|
|
grow_col = 0;
|
|
else
|
|
num_columns++;
|
|
|
|
if (extra_column) {
|
|
num_columns++;
|
|
grow_col++;
|
|
}
|
|
|
|
m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1, 0);
|
|
static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxBOTH);
|
|
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(grow_col);
|
|
|
|
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, !staticbox ? 0 : 5);
|
|
|
|
// activate lines
|
|
for (Line& line : m_lines) {
|
|
throw_if_canceled();
|
|
activate_line(line);
|
|
}
|
|
|
|
ctrl_horiz_alignment = horiz_alignment;
|
|
if (custom_ctrl)
|
|
custom_ctrl->init_max_win_width();
|
|
} catch (UIBuildCanceled&) {
|
|
auto p = sizer;
|
|
this->clear();
|
|
p->Clear(true);
|
|
delete p;
|
|
throw;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void free_window(wxWindow* win);
|
|
|
|
// delete all controls from the option group
|
|
void OptionsGroup::clear(bool destroy_custom_ctrl)
|
|
{
|
|
if (!sizer)
|
|
return;
|
|
|
|
m_grid_sizer = nullptr;
|
|
sizer = nullptr;
|
|
stb = nullptr; // BBS: fix pointer
|
|
|
|
for (Line& line : m_lines) {
|
|
if (line.near_label_widget_win)
|
|
line.near_label_widget_win = nullptr;
|
|
line.label_widget = nullptr;
|
|
|
|
if (line.widget_sizer) {
|
|
line.widget_sizer->Clear(true);
|
|
line.widget_sizer = nullptr;
|
|
}
|
|
|
|
if (line.extra_widget_sizer) {
|
|
line.extra_widget_sizer->Clear(true);
|
|
line.extra_widget_sizer = nullptr;
|
|
}
|
|
}
|
|
|
|
if (custom_ctrl) {
|
|
for (auto const& item : m_fields) {
|
|
wxWindow* win = item.second.get()->getWindow();
|
|
if (win) {
|
|
free_window(win);
|
|
win = nullptr;
|
|
}
|
|
}
|
|
// BBS: custom_ctrl already destroyed from sizer->clear(), no need to destroy here anymore
|
|
if (destroy_custom_ctrl)
|
|
// custom_ctrl->Destroy();
|
|
custom_ctrl = nullptr;
|
|
else
|
|
custom_ctrl = nullptr;
|
|
}
|
|
|
|
m_extra_column_item_ptrs.clear();
|
|
m_fields.clear();
|
|
}
|
|
|
|
Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path /* = std::string()*/) const
|
|
{
|
|
wxString tooltip = _(get_formatted_tooltip_text(option.opt, option.opt_id));
|
|
|
|
Line retval{_(option.opt.label), tooltip};
|
|
retval.label_path = path;
|
|
retval.append_option(option);
|
|
|
|
return retval;
|
|
}
|
|
|
|
void OptionsGroup::clear_fields_except_of(const std::vector<std::string> left_fields)
|
|
{
|
|
auto it = m_fields.begin();
|
|
while (it != m_fields.end()) {
|
|
if (std::find(left_fields.begin(), left_fields.end(), it->first) == left_fields.end())
|
|
it = m_fields.erase(it);
|
|
else
|
|
it++;
|
|
}
|
|
}
|
|
|
|
void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
|
|
{
|
|
if (m_on_change != nullptr)
|
|
m_on_change(opt_id, value);
|
|
}
|
|
|
|
Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
|
|
{
|
|
if (!m_config->has(opt_key)) {
|
|
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n";
|
|
}
|
|
|
|
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
|
|
std::pair<std::string, int> pair(opt_key, opt_index);
|
|
m_opt_map.emplace(opt_id, pair);
|
|
|
|
if (m_use_custom_ctrl) // fill group and category values just for options from Settings Tab
|
|
wxGetApp().sidebar().settings_index().add_key(opt_id, static_cast<Preset::Type>(this->config_type()), title, this->config_category(), this->icon);
|
|
|
|
return Option(*m_config->def()->get(opt_key), opt_id);
|
|
}
|
|
|
|
void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
|
|
{
|
|
if (opt_id == "printer_agent") {
|
|
// TODO: Replace this option-specific branch with a generic value adapter if
|
|
// more fields need custom field-value to config-value conversion.
|
|
if (const std::string* id = boost::any_cast<std::string>(&value))
|
|
this->change_opt_value("printer_agent", wxGetApp().canonical_printer_agent_id(*id));
|
|
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
return;
|
|
}
|
|
|
|
if (!m_opt_map.empty()) {
|
|
auto it = m_opt_map.find(opt_id);
|
|
if (it == m_opt_map.end()) {
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
// BBS
|
|
if (opt_id == "bed_temperature" || opt_id == "bed_temperature_initial_layer") {
|
|
if (m_modelconfig)
|
|
m_modelconfig->touch();
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
auto itOption = it->second;
|
|
const std::string& opt_key = itOption.first;
|
|
int opt_index = itOption.second;
|
|
this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index);
|
|
}
|
|
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
}
|
|
|
|
std::string OptionsGroup::pick_plugin(const ConfigOptionDef& opt)
|
|
{
|
|
Slic3r::PluginManager& manager = Slic3r::PluginManager::instance();
|
|
const Slic3r::PluginCapabilityType plugin_type = Slic3r::plugin_capability_type_from_string(opt.plugin_type);
|
|
if (plugin_type == Slic3r::PluginCapabilityType::Unknown) {
|
|
MessageDialog dlg(m_parent,
|
|
opt.plugin_type.empty() ? _L("This setting does not specify a plugin capability type.")
|
|
: _L("This setting specifies an unrecognized plugin capability type: ") + "'" + opt.plugin_type + "'.",
|
|
_L("Plugin Selection"),
|
|
wxOK | wxICON_WARNING
|
|
);
|
|
dlg.CenterOnParent();
|
|
dlg.ShowModal();
|
|
|
|
return {};
|
|
}
|
|
|
|
struct CapabilityRef { std::string plugin_key; std::string name; };
|
|
std::vector<CapabilityRef> caps;
|
|
for (const auto& capability : manager.get_plugin_capabilities(/*plugin_key=*/"", plugin_type, /*only_enabled=*/true))
|
|
caps.push_back({capability->audit_plugin_key(), capability->name()});
|
|
std::sort(caps.begin(), caps.end(), [](const CapabilityRef& lhs, const CapabilityRef& rhs) {
|
|
return lhs.name == rhs.name ? lhs.plugin_key < rhs.plugin_key : lhs.name < rhs.name;
|
|
});
|
|
|
|
if (caps.empty()) {
|
|
MessageDialog dlg(m_parent,
|
|
_L("No plugins capabilities available for this type.\nEnable or install some to use."),
|
|
_L("Plugin Selection"),
|
|
wxOK | wxICON_INFORMATION
|
|
);
|
|
dlg.CenterOnParent();
|
|
dlg.ShowModal();
|
|
return {};
|
|
}
|
|
|
|
std::vector<PluginPickerDialog::CapabilityEntry> entries;
|
|
entries.reserve(caps.size());
|
|
for (const auto& cap : caps) {
|
|
Slic3r::PluginDescriptor descriptor;
|
|
wxString package_name = manager.try_get_plugin_descriptor(cap.plugin_key, descriptor)
|
|
? from_u8(descriptor.name) : from_u8(cap.plugin_key);
|
|
entries.push_back({
|
|
cap.plugin_key, cap.name, from_u8(cap.name) + from_u8(" \xE2\x80\x94 ") + package_name, package_name
|
|
});
|
|
}
|
|
|
|
PluginPickerDialog dlg(m_parent, from_u8(opt.plugin_type), std::move(entries));
|
|
if (dlg.ShowModal() != wxID_OK)
|
|
return {};
|
|
const auto selection = dlg.selected_capability();
|
|
if (selection.plugin_key.empty() || selection.name.empty())
|
|
return {};
|
|
|
|
// Only the bare capability name is stored; the full "name;uuid;capability" reference is derived when
|
|
// the preset is serialized (see ConfigBase::save_plugin_collection). Resolve here to reject bad picks.
|
|
Slic3r::PluginDescriptor descriptor;
|
|
if (!manager.try_get_plugin_descriptor(selection.plugin_key, descriptor) || descriptor.name.empty())
|
|
return {};
|
|
|
|
BOOST_LOG_TRIVIAL(info) << "Picked plugin capability: " << selection.name;
|
|
return selection.name;
|
|
}
|
|
|
|
void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key)
|
|
{
|
|
if (m_get_initial_config == nullptr)
|
|
return;
|
|
back_to_config_value(m_get_initial_config(), opt_key);
|
|
}
|
|
|
|
void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key)
|
|
{
|
|
if (m_get_sys_config == nullptr)
|
|
return;
|
|
if (!have_sys_config())
|
|
return;
|
|
back_to_config_value(m_get_sys_config(), opt_key);
|
|
}
|
|
|
|
void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key)
|
|
{
|
|
boost::any value;
|
|
if (opt_key == "extruders_count") {
|
|
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
|
|
value = int(nozzle_diameter->values.size());
|
|
}
|
|
#if 0
|
|
// BBS
|
|
else if (opt_key == "bed_temperature" || opt_key == "bed_temperature_initial_layer") {
|
|
// BBS: config is preset initial value, not presets.m_edited_preset,
|
|
// so bed_type value does not contains modification.
|
|
//int bed_type = config.opt_enum("bed_type", 0);
|
|
if (!this->get_field("bed_type")) {
|
|
value = 0;
|
|
throw Slic3r::InvalidArgument("Too old 3MF which has no bed_type field.");
|
|
} else {
|
|
int bed_type = boost::any_cast<int>(this->get_field("bed_type")->get_value());
|
|
const ConfigOptionInts* bed_temps = dynamic_cast<const ConfigOptionInts*>(config.option(opt_key));
|
|
value = bed_type < bed_temps->size() ? bed_temps->get_at(bed_type) : 0;
|
|
}
|
|
}
|
|
#endif
|
|
else if (opt_key == "printer_agent")
|
|
{
|
|
// A deregistered/"(missing)" saved id has no selectable row, so the field yields no
|
|
// value. Restore the saved id directly instead of letting the generic revert path read
|
|
// the field value back into the edited config.
|
|
const std::string saved_id = config.opt_string("printer_agent");
|
|
set_value(opt_key, saved_id);
|
|
this->change_opt_value(opt_key, saved_id);
|
|
OptionsGroup::on_change_OG(opt_key, saved_id);
|
|
return;
|
|
}
|
|
else if (m_opt_map.find(opt_key) == m_opt_map.end() ||
|
|
// This option don't have corresponded field
|
|
opt_key == "printable_area" || opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "thumbnails" ||
|
|
opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") {
|
|
value = get_config_value(config, opt_key);
|
|
set_value(opt_key, value);
|
|
this->change_opt_value(opt_key, get_value(opt_key));
|
|
OptionsGroup::on_change_OG(opt_key, get_value(opt_key));
|
|
return;
|
|
} else {
|
|
auto opt_id = m_opt_map.find(opt_key)->first;
|
|
std::string opt_short_key = m_opt_map.at(opt_id).first;
|
|
int opt_index = m_opt_map.at(opt_id).second;
|
|
value = get_config_value(config, opt_short_key, opt_index);
|
|
}
|
|
|
|
// BBS: restore all pages in preset
|
|
if (set_value(opt_key, value))
|
|
on_change_OG(opt_key, get_value(opt_key));
|
|
else if (m_opt_map.find(opt_key) != m_opt_map.end()) {
|
|
auto opt_id = m_opt_map.find(opt_key)->first;
|
|
std::string opt_short_key = m_opt_map.at(opt_id).first;
|
|
int opt_index = m_opt_map.at(opt_id).second;
|
|
on_change_OG(opt_key, get_config_value2(config, opt_short_key, opt_index));
|
|
}
|
|
}
|
|
|
|
void ConfigOptionsGroup::on_kill_focus(const std::string& opt_key)
|
|
{
|
|
if (m_fill_empty_value)
|
|
m_fill_empty_value(opt_key);
|
|
else
|
|
reload_config();
|
|
}
|
|
|
|
void ConfigOptionsGroup::reload_config()
|
|
{
|
|
for (auto& kvp : m_opt_map) {
|
|
// Name of the option field (name of the configuration key, possibly suffixed with '#' and the index of a scalar inside a vector.
|
|
const std::string& opt_id = kvp.first;
|
|
// option key (may be scalar or vector)
|
|
const std::string& opt_key = kvp.second.first;
|
|
// index in the vector option, zero for scalars
|
|
int opt_index = kvp.second.second;
|
|
const ConfigOptionDef& option = m_options.at(opt_id).opt;
|
|
this->set_value(opt_id, config_value(opt_key, opt_index, option.gui_flags == "serialized"));
|
|
}
|
|
}
|
|
|
|
void ConfigOptionsGroup::Hide() { Show(false); }
|
|
|
|
void ConfigOptionsGroup::Show(const bool show)
|
|
{
|
|
sizer->ShowItems(show);
|
|
#if 0 // #ifdef __WXGTK__
|
|
m_panel->Show(show);
|
|
m_grid_sizer->Show(show);
|
|
#endif /* __WXGTK__ */
|
|
}
|
|
|
|
bool ConfigOptionsGroup::is_visible(ConfigOptionMode mode)
|
|
{
|
|
if (m_options_mode.empty())
|
|
return true;
|
|
if (m_options_mode.size() == 1)
|
|
return m_options_mode[0] <= mode;
|
|
|
|
size_t hidden_row_cnt = 0;
|
|
for (auto opt_mode : m_options_mode)
|
|
if (opt_mode > mode)
|
|
hidden_row_cnt++;
|
|
|
|
return hidden_row_cnt != m_options_mode.size();
|
|
}
|
|
|
|
bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode)
|
|
{
|
|
if (m_options_mode.empty() || !m_grid_sizer)
|
|
return true;
|
|
|
|
if (custom_ctrl) {
|
|
bool show = custom_ctrl->update_visibility(mode);
|
|
this->Show(show);
|
|
return show;
|
|
}
|
|
|
|
int opt_mode_size = m_options_mode.size();
|
|
if (m_grid_sizer->GetEffectiveRowsCount() != opt_mode_size && opt_mode_size == 1)
|
|
return m_options_mode[0] <= mode;
|
|
|
|
Show(true);
|
|
|
|
int coef = 0;
|
|
int hidden_row_cnt = 0;
|
|
const int cols = m_grid_sizer->GetCols();
|
|
Line* line = &m_lines.front();
|
|
size_t line_opt_end = line->get_options().size();
|
|
for (auto opt_mode : m_options_mode) {
|
|
const bool show = opt_mode <= mode && line->toggle_visible;
|
|
if (--line_opt_end == 0) {
|
|
++line;
|
|
line_opt_end = line->get_options().size();
|
|
}
|
|
if (!show) {
|
|
hidden_row_cnt++;
|
|
for (int i = 0; i < cols; ++i)
|
|
m_grid_sizer->Show(coef + i, show);
|
|
}
|
|
coef += cols;
|
|
}
|
|
|
|
if (hidden_row_cnt == opt_mode_size) {
|
|
sizer->ShowItems(false);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ConfigOptionsGroup::msw_rescale()
|
|
{
|
|
// update bitmaps for extra column items (like "mode markers" or buttons on settings panel)
|
|
if (rescale_extra_column_item)
|
|
for (auto extra_col : m_extra_column_item_ptrs)
|
|
rescale_extra_column_item(extra_col);
|
|
|
|
// update undo buttons : rescale bitmaps
|
|
for (const auto& field : m_fields)
|
|
field.second->msw_rescale();
|
|
|
|
auto rescale = [](wxSizer* sizer) {
|
|
for (wxSizerItem* item : sizer->GetChildren())
|
|
if (item->IsWindow()) {
|
|
wxWindow* win = item->GetWindow();
|
|
// check if window is ScalableButton
|
|
ScalableButton* sc_btn = dynamic_cast<ScalableButton*>(win);
|
|
if (sc_btn) {
|
|
sc_btn->msw_rescale();
|
|
sc_btn->SetSize(sc_btn->GetBestSize());
|
|
return;
|
|
}
|
|
// check if window is wxButton
|
|
wxButton* btn = dynamic_cast<wxButton*>(win);
|
|
if (btn) {
|
|
btn->SetSize(btn->GetBestSize());
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
// scale widgets and extra widgets if any exists
|
|
for (const Line& line : m_lines) {
|
|
if (line.widget_sizer)
|
|
rescale(line.widget_sizer);
|
|
if (line.extra_widget_sizer)
|
|
rescale(line.extra_widget_sizer);
|
|
}
|
|
|
|
if (custom_ctrl)
|
|
custom_ctrl->msw_rescale();
|
|
|
|
if (auto line = dynamic_cast<::StaticLine*>(stb))
|
|
line->Rescale();
|
|
}
|
|
|
|
void ConfigOptionsGroup::sys_color_changed()
|
|
{
|
|
#ifdef _WIN32
|
|
if (staticbox && stb) {
|
|
wxGetApp().UpdateAllStaticTextDarkUI(stb);
|
|
// update bitmaps for extra column items (like "delete" buttons on settings panel)
|
|
for (auto extra_col : m_extra_column_item_ptrs)
|
|
wxGetApp().UpdateDarkUI(extra_col);
|
|
}
|
|
|
|
if (custom_ctrl)
|
|
wxGetApp().UpdateDarkUI(custom_ctrl);
|
|
#endif
|
|
|
|
auto update = [](wxSizer* sizer) {
|
|
for (wxSizerItem* item : sizer->GetChildren())
|
|
if (item->IsWindow()) {
|
|
wxWindow* win = item->GetWindow();
|
|
// check if window is ScalableButton
|
|
if (ScalableButton* sc_btn = dynamic_cast<ScalableButton*>(win)) {
|
|
sc_btn->msw_rescale();
|
|
return;
|
|
}
|
|
wxGetApp().UpdateDarkUI(win, dynamic_cast<wxButton*>(win) != nullptr);
|
|
}
|
|
};
|
|
|
|
// scale widgets and extra widgets if any exists
|
|
for (const Line& line : m_lines) {
|
|
if (line.widget_sizer)
|
|
update(line.widget_sizer);
|
|
if (line.extra_widget_sizer)
|
|
update(line.extra_widget_sizer);
|
|
}
|
|
|
|
// update undo buttons : rescale bitmaps
|
|
for (const auto& field : m_fields)
|
|
field.second->sys_color_changed();
|
|
}
|
|
|
|
void ConfigOptionsGroup::refresh()
|
|
{
|
|
if (custom_ctrl)
|
|
custom_ctrl->Refresh();
|
|
}
|
|
|
|
boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize)
|
|
{
|
|
if (opt_key == "bed_type")
|
|
return boost::any((int) BedType::btPC);
|
|
|
|
if (deserialize) {
|
|
// Want to edit a vector value(currently only multi - strings) in a single edit box.
|
|
// Aggregate the strings the old way.
|
|
// Currently used for the post_process config value only.
|
|
if (opt_index != -1)
|
|
throw Slic3r::OutOfRange("Can't deserialize option indexed value");
|
|
// return join(';', m_config->get(opt_key)});
|
|
return get_config_value(*m_config, opt_key);
|
|
} else {
|
|
// return opt_index == -1 ? m_config->get(opt_key) : m_config->get_at(opt_key, opt_index);
|
|
return get_config_value(*m_config, opt_key, opt_index);
|
|
}
|
|
}
|
|
|
|
boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index /*= -1*/)
|
|
{
|
|
size_t idx = opt_index == -1 ? 0 : opt_index;
|
|
|
|
boost::any ret;
|
|
wxString text_value = wxString("");
|
|
const ConfigOptionDef* opt = config.def()->get(opt_key);
|
|
|
|
if (opt->nullable) {
|
|
switch (opt->type) {
|
|
case coPercents:
|
|
case coFloats: {
|
|
if (opt_index < 0 ? config.option(opt_key)->is_nil() :
|
|
dynamic_cast<ConfigOptionVectorBase const*>(config.option(opt_key))->is_nil(opt_index))
|
|
ret = _(L("N/A"));
|
|
else {
|
|
double val = opt->type == coFloats ? config.option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx) :
|
|
config.option<ConfigOptionPercentsNullable>(opt_key)->get_at(idx);
|
|
ret = double_to_string(val);
|
|
}
|
|
break;
|
|
}
|
|
case coFloatsOrPercents: {
|
|
if (opt_index < 0 ? config.option(opt_key)->is_nil() :
|
|
dynamic_cast<ConfigOptionVectorBase const*>(config.option(opt_key))->is_nil(opt_index))
|
|
ret = _(L("N/A"));
|
|
else {
|
|
const auto& value = config.option<ConfigOptionFloatsOrPercentsNullable>(opt_key)->get_at(idx);
|
|
text_value = double_to_string(value.value);
|
|
if (value.percent)
|
|
text_value += "%";
|
|
|
|
ret = text_value;
|
|
}
|
|
break;
|
|
}
|
|
case coBools: ret = config.option<ConfigOptionBoolsNullable>(opt_key)->values[idx]; break;
|
|
case coInts: ret = config.option<ConfigOptionIntsNullable>(opt_key)->get_at(idx); break;
|
|
case coEnums: ret = config.option<ConfigOptionEnumsGenericNullable>(opt_key)->get_at(idx); break;
|
|
default: break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
switch (opt->type) {
|
|
case coFloatOrPercent: {
|
|
const auto& value = *config.option<ConfigOptionFloatOrPercent>(opt_key);
|
|
|
|
text_value = double_to_string(value.value);
|
|
if (value.percent)
|
|
text_value += "%";
|
|
|
|
ret = text_value;
|
|
break;
|
|
}
|
|
case coFloatsOrPercents: {
|
|
const auto& value = config.option<ConfigOptionFloatsOrPercents>(opt_key)->get_at(idx);
|
|
|
|
text_value = double_to_string(value.value);
|
|
if (value.percent)
|
|
text_value += "%";
|
|
|
|
ret = text_value;
|
|
break;
|
|
}
|
|
case coPercent: {
|
|
double val = config.option<ConfigOptionPercent>(opt_key)->value;
|
|
ret = double_to_string(val); // += "%";
|
|
} break;
|
|
case coPercents:
|
|
case coFloats:
|
|
case coFloat: {
|
|
if (opt_key == "extruder_printable_height") {
|
|
auto opt_values = dynamic_cast<const ConfigOptionFloatsNullable*>(config.option(opt_key))->values;
|
|
if (!opt_values.empty()) {
|
|
double val = opt_values[idx];
|
|
ret = double_to_string(val);
|
|
}
|
|
} else {
|
|
double val = opt->type == coFloats ? config.opt_float(opt_key, idx) :
|
|
opt->type == coFloat ? config.opt_float(opt_key) :
|
|
config.option<ConfigOptionPercents>(opt_key)->get_at(idx);
|
|
ret = double_to_string(val);
|
|
}
|
|
break;
|
|
}
|
|
case coString: ret = from_u8(config.opt_string(opt_key)); break;
|
|
case coStrings:
|
|
if (opt_key == "compatible_printers" || opt_key == "compatible_prints") {
|
|
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
break;
|
|
}
|
|
if (opt->gui_type == ConfigOptionDef::GUIType::plugin_picker) {
|
|
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
break;
|
|
}
|
|
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
|
ret = text_value;
|
|
else if (opt->gui_flags == "serialized") {
|
|
std::vector<std::string> values = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
if (!values.empty() && !values[0].empty())
|
|
for (auto el : values)
|
|
text_value += el + ";";
|
|
ret = text_value;
|
|
} else
|
|
ret = from_u8(config.opt_string(opt_key, static_cast<unsigned int>(idx)));
|
|
break;
|
|
case coBool: ret = config.opt_bool(opt_key); break;
|
|
case coBools: ret = config.opt_bool(opt_key, idx); break;
|
|
case coInt: ret = config.opt_int(opt_key); break;
|
|
case coInts: ret = config.opt_int(opt_key, idx); break;
|
|
case coEnum:
|
|
if (!config.has("first_layer_sequence_choice") && opt_key == "first_layer_sequence_choice") {
|
|
// reset to Auto value
|
|
ret = 0;
|
|
break;
|
|
}
|
|
if (!config.has("other_layers_sequence_choice") && opt_key == "other_layers_sequence_choice") {
|
|
// reset to Auto value
|
|
ret = 0;
|
|
break;
|
|
}
|
|
if (!config.has("curr_bed_type") && opt_key == "curr_bed_type") {
|
|
// reset to global value
|
|
DynamicConfig& global_cfg = wxGetApp().preset_bundle->project_config;
|
|
ret = global_cfg.option("curr_bed_type")->getInt();
|
|
break;
|
|
}
|
|
ret = config.option(opt_key)->getInt();
|
|
break;
|
|
// BBS
|
|
case coEnums: ret = config.opt_int(opt_key, idx); break;
|
|
case coPoint: ret = config.option<ConfigOptionPoint>(opt_key)->value; break;
|
|
case coPoints:
|
|
if (opt_key == "printable_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else if (opt_key == "bed_exclude_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else if (opt_key == "wrapping_exclude_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else
|
|
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
|
|
break;
|
|
case coPointsGroups:
|
|
if (opt_key == "extruder_printable_area") {
|
|
auto values = config.option<ConfigOptionPointsGroups>(opt_key)->values;
|
|
if (!values.empty())
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPointsGroups>(opt_key)->get_at(idx));
|
|
} else
|
|
ret = config.option<ConfigOptionPointsGroups>(opt_key)->get_at(idx);
|
|
break;
|
|
case coNone:
|
|
default: break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// BBS: restore all pages in preset
|
|
boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index /*= -1*/)
|
|
{
|
|
size_t idx = opt_index == -1 ? 0 : opt_index;
|
|
|
|
boost::any ret;
|
|
const ConfigOptionDef* opt = config.def()->get(opt_key);
|
|
|
|
if (opt->nullable) {
|
|
switch (opt->type) {
|
|
case coPercents:
|
|
case coFloats: {
|
|
if (config.option(opt_key)->is_nil())
|
|
ret = ConfigOptionFloatsNullable::nil_value();
|
|
else {
|
|
double val = opt->type == coFloats ? config.option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx) :
|
|
config.option<ConfigOptionPercentsNullable>(opt_key)->get_at(idx);
|
|
ret = val;
|
|
}
|
|
} break;
|
|
case coFloatsOrPercents: {
|
|
if (config.option(opt_key)->is_nil())
|
|
ret = ConfigOptionFloatsOrPercentsNullable::nil_value();
|
|
else {
|
|
const auto& value = config.option<ConfigOptionFloatsOrPercentsNullable>(opt_key)->get_at(idx);
|
|
wxString text_value = double_to_string(value.value);
|
|
if (value.percent)
|
|
text_value += "%";
|
|
|
|
ret = text_value;
|
|
}
|
|
break;
|
|
}
|
|
case coBools: ret = config.option<ConfigOptionBoolsNullable>(opt_key)->values[idx]; break;
|
|
case coInts: ret = config.option<ConfigOptionIntsNullable>(opt_key)->get_at(idx); break;
|
|
default: break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
switch (opt->type) {
|
|
case coFloatOrPercent: {
|
|
const auto& value = *config.option<ConfigOptionFloatOrPercent>(opt_key);
|
|
|
|
wxString text_value = double_to_string(value.value);
|
|
if (value.percent)
|
|
text_value += "%";
|
|
|
|
ret = into_u8(text_value);
|
|
break;
|
|
}
|
|
case coPercent: {
|
|
double val = config.option<ConfigOptionPercent>(opt_key)->value;
|
|
ret = val;
|
|
} break;
|
|
case coPercents:
|
|
case coFloats:
|
|
case coFloat: {
|
|
double val = opt->type == coFloats ? config.opt_float(opt_key, idx) :
|
|
opt->type == coFloat ? config.opt_float(opt_key) :
|
|
config.option<ConfigOptionPercents>(opt_key)->get_at(idx);
|
|
ret = val;
|
|
} break;
|
|
case coString: ret = config.opt_string(opt_key); break;
|
|
case coStrings:
|
|
if (opt_key == "compatible_printers" || opt_key == "compatible_prints") {
|
|
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
break;
|
|
}
|
|
if (opt->gui_type == ConfigOptionDef::GUIType::plugin_picker) {
|
|
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
break;
|
|
}
|
|
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
|
ret = std::string();
|
|
else if (opt->gui_flags == "serialized") {
|
|
ret = config.option<ConfigOptionStrings>(opt_key)->values;
|
|
} else
|
|
ret = config.opt_string(opt_key, static_cast<unsigned int>(idx));
|
|
break;
|
|
case coBool: ret = config.opt_bool(opt_key); break;
|
|
case coBools: ret = static_cast<unsigned char>(config.opt_bool(opt_key, idx)); break;
|
|
case coInt: ret = config.opt_int(opt_key); break;
|
|
case coInts: ret = config.opt_int(opt_key, idx); break;
|
|
case coEnum: ret = config.option(opt_key)->getInt(); break;
|
|
case coEnums: ret = config.opt_int(opt_key, idx); break;
|
|
case coPoint: ret = config.option<ConfigOptionPoint>(opt_key)->value; break;
|
|
case coPoints:
|
|
if (opt_key == "printable_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else if (opt_key == "bed_exclude_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else if (opt_key == "wrapping_exclude_area")
|
|
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
|
else
|
|
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
|
|
break;
|
|
case coNone:
|
|
default: break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index)
|
|
{
|
|
Field* field = get_field(opt_key);
|
|
if (field != nullptr)
|
|
return field;
|
|
std::string opt_id = opt_key + '#' + std::to_string(opt_index);
|
|
return get_field(opt_id);
|
|
}
|
|
|
|
std::pair<OG_CustomCtrl*, bool*> ConfigOptionsGroup::get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key,
|
|
int opt_index /* = -1*/)
|
|
{
|
|
Field* field = get_fieldc(opt_key, opt_index);
|
|
|
|
if (field)
|
|
return {custom_ctrl, field->get_blink_ptr()};
|
|
|
|
for (Line& line : m_lines)
|
|
for (const Option& opt : line.get_options())
|
|
if (opt.opt_id == opt_key && line.widget)
|
|
return {custom_ctrl, line.get_blink_ptr()};
|
|
|
|
return {nullptr, nullptr};
|
|
}
|
|
|
|
// Change an option on m_config, possibly call ModelConfig::touch().
|
|
void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
|
|
|
|
{
|
|
Slic3r::GUI::change_opt_value(const_cast<DynamicPrintConfig&>(*m_config), opt_key, value, opt_index);
|
|
if (m_modelconfig)
|
|
m_modelconfig->touch();
|
|
}
|
|
|
|
// BBS
|
|
void ExtruderOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
|
|
{
|
|
if (!m_opt_map.empty()) {
|
|
auto it = m_opt_map.find(opt_id);
|
|
if (it == m_opt_map.end()) {
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
return;
|
|
}
|
|
|
|
auto itOption = it->second;
|
|
const std::string& opt_key = itOption.first;
|
|
|
|
int opt_index = itOption.second;
|
|
this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index);
|
|
}
|
|
|
|
OptionsGroup::on_change_OG(opt_id, value);
|
|
}
|
|
|
|
wxString OptionsGroup::get_url(const std::string& path_end)
|
|
{
|
|
// BBS
|
|
wxString str = from_u8(path_end);
|
|
auto pos = str.find(L'#');
|
|
if (pos != size_t(-1)) {
|
|
pos++;
|
|
wxString anchor = str.Mid(pos).Lower();
|
|
anchor.Replace(L" ", "-");
|
|
str = str.Left(pos) + anchor;
|
|
}
|
|
// Orca: point to sf wiki for seam parameters
|
|
return wxString::Format(L"https://www.orcaslicer.com/wiki/%s", from_u8(path_end));
|
|
}
|
|
|
|
bool OptionsGroup::launch_browser(const std::string& path_end) { return wxLaunchDefaultBrowser(OptionsGroup::get_url(path_end)); }
|
|
|
|
//-------------------------------------------------------------------------------------------
|
|
// ogStaticText
|
|
//-------------------------------------------------------------------------------------------
|
|
|
|
ogStaticText::ogStaticText(wxWindow* parent, const wxString& text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize)
|
|
{
|
|
if (!text.IsEmpty()) {
|
|
Wrap(60 * wxGetApp().em_unit());
|
|
GetParent()->Layout();
|
|
}
|
|
}
|
|
|
|
void ogStaticText::SetText(const wxString& value, bool wrap /* = true*/)
|
|
{
|
|
SetLabel(value);
|
|
if (wrap)
|
|
Wrap(60 * wxGetApp().em_unit());
|
|
GetParent()->Layout();
|
|
}
|
|
|
|
void ogStaticText::SetPathEnd(const std::string& link)
|
|
{
|
|
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) {
|
|
if (HasCapture())
|
|
return;
|
|
this->CaptureMouse();
|
|
event.Skip();
|
|
});
|
|
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
|
if (!HasCapture())
|
|
return;
|
|
ReleaseMouse();
|
|
// BBS
|
|
// OptionsGroup::launch_browser(link);
|
|
event.Skip();
|
|
});
|
|
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
|
SetToolTip(OptionsGroup::get_url(std::string()));
|
|
FocusText(true);
|
|
event.Skip();
|
|
});
|
|
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) {
|
|
FocusText(false);
|
|
event.Skip();
|
|
});
|
|
}
|
|
|
|
void ogStaticText::FocusText(bool focus)
|
|
{
|
|
SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() : Slic3r::GUI::wxGetApp().normal_font());
|
|
Refresh();
|
|
}
|
|
|
|
}} // namespace Slic3r::GUI
|