PrusaSlicer-NonPlainar/src/slic3r/GUI/Preferences.cpp

1095 lines
41 KiB
C++

#include "Preferences.hpp"
#include "OptionsGroup.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "MsgDialog.hpp"
#include "I18N.hpp"
#include "libslic3r/AppConfig.hpp"
#include <wx/notebook.h>
#include "Notebook.hpp"
#include "ButtonsDescription.hpp"
#include "OG_CustomCtrl.hpp"
#include "GLCanvas3D.hpp"
#include "ConfigWizard_private.hpp"
#include <boost/dll/runtime_symbol_info.hpp>
#ifdef WIN32
#include <wx/msw/registry.h>
#endif // WIN32
#ifdef __linux__
#include "DesktopIntegrationDialog.hpp"
#endif //__linux__
namespace Slic3r {
static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values& enum_keys_map)
{
t_config_enum_names names;
int cnt = 0;
for (const auto& kvp : enum_keys_map)
cnt = std::max(cnt, kvp.second);
cnt += 1;
names.assign(cnt, "");
for (const auto& kvp : enum_keys_map)
names[kvp.second] = kvp.first;
return names;
}
#define CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NAME) \
static t_config_enum_names s_keys_names_##NAME = enum_names_from_keys_map(s_keys_map_##NAME); \
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values() { return s_keys_map_##NAME; } \
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names() { return s_keys_names_##NAME; }
static const t_config_enum_values s_keys_map_NotifyReleaseMode = {
{"all", NotifyReleaseAll},
{"release", NotifyReleaseOnly},
{"none", NotifyReleaseNone},
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NotifyReleaseMode)
namespace GUI {
PreferencesDialog::PreferencesDialog(wxWindow* parent) :
DPIDialog(parent, wxID_ANY, _L("Preferences"), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
#ifdef __WXOSX__
isOSX = true;
#endif
build();
m_highlighter.set_timer_owner(this, 0);
}
static void update_color(wxColourPickerCtrl* color_pckr, const wxColour& color)
{
if (color_pckr->GetColour() != color) {
color_pckr->SetColour(color);
wxPostEvent(color_pckr, wxCommandEvent(wxEVT_COLOURPICKER_CHANGED));
}
}
void PreferencesDialog::show(const std::string& highlight_opt_key /*= std::string()*/, const std::string& tab_name/*= std::string()*/)
{
int selected_tab = 0;
for ( ; selected_tab < int(tabs->GetPageCount()); selected_tab++)
if (tabs->GetPageText(selected_tab) == _(tab_name))
break;
if (selected_tab < int(tabs->GetPageCount()))
tabs->SetSelection(selected_tab);
if (!highlight_opt_key.empty())
init_highlighter(highlight_opt_key);
// cache input values for custom toolbar size
m_custom_toolbar_size = atoi(get_app_config()->get("custom_toolbar_size").c_str());
m_use_custom_toolbar_size = get_app_config()->get("use_custom_toolbar_size") == "1";
if (wxGetApp().is_editor()) {
auto app_config = get_app_config();
downloader->set_path_name(app_config->get("url_downloader_dest"));
downloader->allow(!app_config->has("downloader_url_registered") || app_config->get("downloader_url_registered") == "1");
for (const std::string& opt_key : {"suppress_hyperlinks", "downloader_url_registered"})
m_optgroup_other->set_value(opt_key, app_config->get(opt_key) == "1");
// update colors for color pickers of the labels
update_color(m_sys_colour, wxGetApp().get_label_clr_sys());
update_color(m_mod_colour, wxGetApp().get_label_clr_modified());
// update color pickers for mode palette
const auto palette = wxGetApp().get_mode_palette();
std::vector<wxColourPickerCtrl*> color_pickres = {m_mode_simple, m_mode_advanced, m_mode_expert};
for (size_t mode = 0; mode < color_pickres.size(); ++mode)
update_color(color_pickres[mode], palette[mode]);
}
this->ShowModal();
}
static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& title, wxBookCtrlBase* tabs)
{
wxPanel* tab = new wxPanel(tabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
tabs->AddPage(tab, _(title));
tab->SetFont(wxGetApp().normal_font());
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->SetSizeHints(tab);
tab->SetSizer(sizer);
std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(tab);
optgroup->label_width = 40;
optgroup->set_config_category_and_type(title, int(Preset::TYPE_PREFERENCES));
return optgroup;
}
static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)
{
optgroup->activate([](){}, wxALIGN_RIGHT);
optgroup->update_visibility(comSimple);
wxBoxSizer* sizer = static_cast<wxBoxSizer*>(static_cast<wxPanel*>(optgroup->parent())->GetSizer());
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
// apply sercher
wxGetApp().sidebar().get_searcher().append_preferences_options(optgroup->get_lines());
}
static void append_bool_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
const std::string& opt_key,
const std::string& label,
const std::string& tooltip,
bool def_val,
ConfigOptionMode mode = comSimple)
{
ConfigOptionDef def = {opt_key, coBool};
def.label = label;
def.tooltip = tooltip;
def.mode = mode;
def.set_default_value(new ConfigOptionBool{ def_val });
Option option(def, opt_key);
optgroup->append_single_option_line(option);
// fill data to the Search Dialog
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
}
static void append_enum_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
const std::string& opt_key,
const std::string& label,
const std::string& tooltip,
const ConfigOption* def_val,
const t_config_enum_values *enum_keys_map,
std::initializer_list<std::string> enum_values,
std::initializer_list<std::string> enum_labels,
ConfigOptionMode mode = comSimple)
{
ConfigOptionDef def = {opt_key, coEnum };
def.label = label;
def.tooltip = tooltip;
def.mode = mode;
def.enum_keys_map = enum_keys_map;
def.enum_values = std::vector<std::string>(enum_values);
def.enum_labels = std::vector<std::string>(enum_labels);
def.set_default_value(def_val);
Option option(def, opt_key);
optgroup->append_single_option_line(option);
// fill data to the Search Dialog
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
}
static void append_string_option(std::shared_ptr<ConfigOptionsGroup> optgroup,
const std::string& opt_key,
const std::string& label,
const std::string& tooltip,
const std::string& def_val,
ConfigOptionMode mode = comSimple)
{
ConfigOptionDef def = { opt_key, coString };
def.label = label;
def.tooltip = tooltip;
def.mode = mode;
def.set_default_value(new ConfigOptionString{ def_val });
Option option(def, opt_key);
optgroup->append_single_option_line(option);
// fill data to the Search Dialog
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
}
static void append_preferences_option_to_searcer(std::shared_ptr<ConfigOptionsGroup> optgroup,
const std::string& opt_key,
const wxString& label)
{
// fill data to the Search Dialog
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
// apply sercher
wxGetApp().sidebar().get_searcher().append_preferences_option(Line(opt_key, label, ""));
}
void PreferencesDialog::build()
{
#ifdef _WIN32
wxGetApp().UpdateDarkUI(this);
#else
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
const wxFont& font = wxGetApp().normal_font();
SetFont(font);
auto app_config = get_app_config();
#ifdef _MSW_DARK_MODE
tabs = new Notebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME | wxNB_DEFAULT);
#else
tabs = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL |wxNB_NOPAGETHEME | wxNB_DEFAULT );
tabs->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif
// Add "General" tab
m_optgroup_general = create_options_tab(L("General"), tabs);
m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (auto it = m_values.find(opt_key); it != m_values.end()) {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
if (opt_key == "default_action_on_close_application" || opt_key == "default_action_on_select_preset" || opt_key == "default_action_on_new_project")
m_values[opt_key] = boost::any_cast<bool>(value) ? "none" : "discard";
else if (opt_key == "default_action_on_dirty_project")
m_values[opt_key] = boost::any_cast<bool>(value) ? "" : "0";
else
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
bool is_editor = wxGetApp().is_editor();
if (is_editor) {
append_bool_option(m_optgroup_general, "remember_output_path",
L("Remember output directory"),
L("If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files."),
app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true);
append_bool_option(m_optgroup_general, "autocenter",
L("Auto-center parts"),
L("If this is enabled, Slic3r will auto-center objects around the print bed center."),
app_config->get("autocenter") == "1");
append_bool_option(m_optgroup_general, "background_processing",
L("Background processing"),
L("If this is enabled, Slic3r will pre-process objects as soon "
"as they\'re loaded in order to save time when exporting G-code."),
app_config->get("background_processing") == "1");
m_optgroup_general->append_separator();
// Please keep in sync with ConfigWizard
append_bool_option(m_optgroup_general, "export_sources_full_pathnames",
L("Export sources full pathnames to 3mf and amf"),
L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked."),
app_config->get("export_sources_full_pathnames") == "1");
#ifdef _WIN32
// Please keep in sync with ConfigWizard
append_bool_option(m_optgroup_general, "associate_3mf",
L("Associate .3mf files to PrusaSlicer"),
L("If enabled, sets PrusaSlicer as default application to open .3mf files."),
app_config->get("associate_3mf") == "1");
append_bool_option(m_optgroup_general, "associate_stl",
L("Associate .stl files to PrusaSlicer"),
L("If enabled, sets PrusaSlicer as default application to open .stl files."),
app_config->get("associate_stl") == "1");
#endif // _WIN32
m_optgroup_general->append_separator();
// Please keep in sync with ConfigWizard
append_bool_option(m_optgroup_general, "preset_update",
L("Update built-in Presets automatically"),
L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded "
"into a separate temporary location. When a new preset version becomes available it is offered at application startup."),
app_config->get("preset_update") == "1");
append_bool_option(m_optgroup_general, "no_defaults",
L("Suppress \" - default - \" presets"),
L("Suppress \" - default - \" presets in the Print / Filament / Printer selections once there are any other valid presets available."),
app_config->get("no_defaults") == "1");
append_bool_option(m_optgroup_general, "show_incompatible_presets",
L("Show incompatible print and filament presets"),
L("When checked, the print and filament presets are shown in the preset editor "
"even if they are marked as incompatible with the active printer"),
app_config->get("show_incompatible_presets") == "1");
m_optgroup_general->append_separator();
append_bool_option(m_optgroup_general, "show_drop_project_dialog",
L("Show drop project dialog"),
L("When checked, whenever dragging and dropping a project file on the application, shows a dialog asking to select the action to take on the file to load."),
app_config->get("show_drop_project_dialog") == "1");
append_bool_option(m_optgroup_general, "single_instance",
#if __APPLE__
L("Allow just a single PrusaSlicer instance"),
L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances "
"of same app from the command line. In such case this settings will allow only one instance."),
#else
L("Allow just a single PrusaSlicer instance"),
L("If this is enabled, when starting PrusaSlicer and another instance of the same PrusaSlicer is already running, that instance will be reactivated instead."),
#endif
app_config->has("single_instance") ? app_config->get("single_instance") == "1" : false );
m_optgroup_general->append_separator();
append_bool_option(m_optgroup_general, "default_action_on_dirty_project",
L("Ask for unsaved changes in project"),
L("Always ask for unsaved changes in project, when: \n"
"- Closing PrusaSlicer,\n"
"- Loading or creating a new project"),
app_config->get("default_action_on_dirty_project").empty());
m_optgroup_general->append_separator();
append_bool_option(m_optgroup_general, "default_action_on_close_application",
L("Ask to save unsaved changes in presets when closing the application or when loading a new project"),
L("Always ask for unsaved changes in presets, when: \n"
"- Closing PrusaSlicer while some presets are modified,\n"
"- Loading a new project while some presets are modified"),
app_config->get("default_action_on_close_application") == "none");
append_bool_option(m_optgroup_general, "default_action_on_select_preset",
L("Ask for unsaved changes in presets when selecting new preset"),
L("Always ask for unsaved changes in presets when selecting new preset or resetting a preset"),
app_config->get("default_action_on_select_preset") == "none");
append_bool_option(m_optgroup_general, "default_action_on_new_project",
L("Ask for unsaved changes in presets when creating new project"),
L("Always ask for unsaved changes in presets when creating new project"),
app_config->get("default_action_on_new_project") == "none");
}
#ifdef _WIN32
else {
append_bool_option(m_optgroup_general, "associate_gcode",
L("Associate .gcode files to PrusaSlicer G-code Viewer"),
L("If enabled, sets PrusaSlicer G-code Viewer as default application to open .gcode files."),
app_config->get("associate_gcode") == "1");
}
#endif // _WIN32
#if __APPLE__
append_bool_option(m_optgroup_general, "use_retina_opengl",
L("Use Retina resolution for the 3D scene"),
L("If enabled, the 3D scene will be rendered in Retina resolution. "
"If you are experiencing 3D performance problems, disabling this option may help."),
app_config->get("use_retina_opengl") == "1");
#endif
m_optgroup_general->append_separator();
// Show/Hide splash screen
append_bool_option(m_optgroup_general, "show_splash_screen",
L("Show splash screen"),
L("Show splash screen"),
app_config->get("show_splash_screen") == "1");
append_bool_option(m_optgroup_general, "restore_win_position",
L("Restore window position on start"),
L("If enabled, PrusaSlicer will be open at the position it was closed"),
app_config->get("restore_win_position") == "1");
// Clear Undo / Redo stack on new project
append_bool_option(m_optgroup_general, "clear_undo_redo_stack_on_new_project",
L("Clear Undo / Redo stack on new project"),
L("Clear Undo / Redo stack on new project or when an existing project is loaded."),
app_config->get("clear_undo_redo_stack_on_new_project") == "1");
#if defined(_WIN32) || defined(__APPLE__)
append_bool_option(m_optgroup_general, "use_legacy_3DConnexion",
L("Enable support for legacy 3DConnexion devices"),
L("If enabled, the legacy 3DConnexion devices settings dialog is available by pressing CTRL+M"),
app_config->get("use_legacy_3DConnexion") == "1");
#endif // _WIN32 || __APPLE__
activate_options_tab(m_optgroup_general);
// Add "Camera" tab
m_optgroup_camera = create_options_tab(L("Camera"), tabs);
m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (auto it = m_values.find(opt_key);it != m_values.end()) {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
append_bool_option(m_optgroup_camera, "use_perspective_camera",
L("Use perspective camera"),
L("If enabled, use perspective camera. If not enabled, use orthographic camera."),
app_config->get("use_perspective_camera") == "1");
append_bool_option(m_optgroup_camera, "use_free_camera",
L("Use free camera"),
L("If enabled, use free camera. If not enabled, use constrained camera."),
app_config->get("use_free_camera") == "1");
append_bool_option(m_optgroup_camera, "reverse_mouse_wheel_zoom",
L("Reverse direction of zoom with mouse wheel"),
L("If enabled, reverses the direction of zoom with mouse wheel"),
app_config->get("reverse_mouse_wheel_zoom") == "1");
activate_options_tab(m_optgroup_camera);
// Add "GUI" tab
m_optgroup_gui = create_options_tab(L("GUI"), tabs);
m_optgroup_gui->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (opt_key == "notify_release") {
int val_int = boost::any_cast<int>(value);
for (const auto& item : s_keys_map_NotifyReleaseMode) {
if (item.second == val_int) {
m_values[opt_key] = item.first;
return;
}
}
}
if (opt_key == "use_custom_toolbar_size") {
m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
refresh_og(m_optgroup_gui);
get_app_config()->set("use_custom_toolbar_size", boost::any_cast<bool>(value) ? "1" : "0");
get_app_config()->save();
wxGetApp().plater()->get_current_canvas3D()->render();
return;
}
if (opt_key == "tabs_as_menu") {
bool disable_new_layout = boost::any_cast<bool>(value);
m_rb_new_settings_layout_mode->Show(!disable_new_layout);
if (disable_new_layout && m_rb_new_settings_layout_mode->GetValue()) {
m_rb_new_settings_layout_mode->SetValue(false);
m_rb_old_settings_layout_mode->SetValue(true);
}
refresh_og(m_optgroup_gui);
}
if (auto it = m_values.find(opt_key); it != m_values.end()) {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
/* if (opt_key == "suppress_hyperlinks")
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
else*/
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
append_bool_option(m_optgroup_gui, "seq_top_layer_only",
L("Sequential slider applied only to top layer"),
L("If enabled, changes made using the sequential slider, in preview, apply only to gcode top layer."
"If disabled, changes made using the sequential slider, in preview, apply to the whole gcode."),
app_config->get("seq_top_layer_only") == "1");
if (is_editor) {
append_bool_option(m_optgroup_gui, "show_collapse_button",
L("Show sidebar collapse/expand button"),
L("If enabled, the button for the collapse sidebar will be appeared in top right corner of the 3D Scene"),
app_config->get("show_collapse_button") == "1");
/*
append_bool_option(m_optgroup_gui, "suppress_hyperlinks",
L("Suppress to open hyperlink in browser"),
L("If enabled, PrusaSlicer will not open a hyperlinks in your browser."),
//L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
// "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
app_config->get("suppress_hyperlinks") == "1");
*/
append_bool_option(m_optgroup_gui, "color_mapinulation_panel",
L("Use colors for axes values in Manipulation panel"),
L("If enabled, the axes names and axes values will be colorized according to the axes colors. "
"If disabled, old UI will be used."),
app_config->get("color_mapinulation_panel") == "1");
append_bool_option(m_optgroup_gui, "order_volumes",
L("Order object volumes by types"),
L("If enabled, volumes will be always ordered inside the object. Correct order is Model Part, Negative Volume, Modifier, Support Blocker and Support Enforcer. "
"If disabled, you can reorder Model Parts, Negative Volumes and Modifiers. But one of the model parts have to be on the first place."),
app_config->get("order_volumes") == "1");
append_bool_option(m_optgroup_gui, "non_manifold_edges",
L("Show non-manifold edges"),
L("If enabled, shows non-manifold edges."),
app_config->get("non_manifold_edges") == "1");
append_bool_option(m_optgroup_gui, "allow_auto_color_change",
L("Allow automatically color change"),
L("If enabled, related notification will be shown, when sliced object looks like a logo or a sign."),
app_config->get("allow_auto_color_change") == "1");
#ifdef _MSW_DARK_MODE
append_bool_option(m_optgroup_gui, "tabs_as_menu",
L("Set settings tabs as menu items (experimental)"),
L("If enabled, Settings Tabs will be placed as menu items. If disabled, old UI will be used."),
app_config->get("tabs_as_menu") == "1");
#endif
m_optgroup_gui->append_separator();
append_bool_option(m_optgroup_gui, "show_hints",
L("Show \"Tip of the day\" notification after start"),
L("If enabled, useful hints are displayed at startup."),
app_config->get("show_hints") == "1");
append_enum_option(m_optgroup_gui, "notify_release",
L("Notify about new releases"),
L("You will be notified about new release after startup acordingly: All = Regular release and alpha / beta releases. Release only = regular release."),
new ConfigOptionEnum<NotifyReleaseMode>(static_cast<NotifyReleaseMode>(s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release")))),
&ConfigOptionEnum<NotifyReleaseMode>::get_enum_values(),
{"all", "release", "none"},
{L("All"), L("Release only"), L("None")});
m_optgroup_gui->append_separator();
append_bool_option(m_optgroup_gui, "use_custom_toolbar_size",
L("Use custom size for toolbar icons"),
L("If enabled, you can change size of toolbar icons manually."),
app_config->get("use_custom_toolbar_size") == "1");
}
activate_options_tab(m_optgroup_gui);
if (is_editor) {
// set Field for notify_release to its value to activate the object
boost::any val = s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release"));
m_optgroup_gui->get_field("notify_release")->set_value(val, false);
create_icon_size_slider();
m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
create_settings_mode_widget();
create_settings_text_color_widget();
create_settings_mode_color_widget();
m_optgroup_other = create_options_tab(_L("Other"), tabs);
m_optgroup_other->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (auto it = m_values.find(opt_key); it != m_values.end() && opt_key != "url_downloader_dest") {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
if (opt_key == "suppress_hyperlinks")
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "";
else
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
append_bool_option(m_optgroup_other, "suppress_hyperlinks",
L("Suppress to open hyperlink in browser"),
L("If enabled, PrusaSlicer will not open a hyperlinks in your browser."),
//L("If enabled, the descriptions of configuration parameters in settings tabs wouldn't work as hyperlinks. "
// "If disabled, the descriptions of configuration parameters in settings tabs will work as hyperlinks."),
app_config->get("suppress_hyperlinks") == "1");
append_bool_option(m_optgroup_other, "downloader_url_registered",
L("Allow downloads from Printables.com"),
L("If enabled, PrusaSlicer will allow to download from Printables.com"),
app_config->get("downloader_url_registered") == "1");
activate_options_tab(m_optgroup_other);
create_downloader_path_sizer();
#if ENABLE_ENVIRONMENT_MAP
// Add "Render" tab
m_optgroup_render = create_options_tab(L("Render"), tabs);
m_optgroup_render->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (auto it = m_values.find(opt_key); it != m_values.end()) {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
append_bool_option(m_optgroup_render, "use_environment_map",
L("Use environment map"),
L("If enabled, renders object using the environment map."),
app_config->get("use_environment_map") == "1");
activate_options_tab(m_optgroup_render);
#endif // ENABLE_ENVIRONMENT_MAP
#ifdef _WIN32
// Add "Dark Mode" tab
m_optgroup_dark_mode = create_options_tab(_L("Dark mode (experimental)"), tabs);
m_optgroup_dark_mode->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
if (auto it = m_values.find(opt_key); it != m_values.end()) {
m_values.erase(it); // we shouldn't change value, if some of those parameters were selected, and then deselected
return;
}
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
append_bool_option(m_optgroup_dark_mode, "dark_color_mode",
L("Enable dark mode"),
L("If enabled, UI will use Dark mode colors. If disabled, old UI will be used."),
app_config->get("dark_color_mode") == "1");
if (wxPlatformInfo::Get().GetOSMajorVersion() >= 10) // Use system menu just for Window newer then Windows 10
// Use menu with ownerdrawn items by default on systems older then Windows 10
{
append_bool_option(m_optgroup_dark_mode, "sys_menu_enabled",
L("Use system menu for application"),
L("If enabled, application will use the standart Windows system menu,\n"
"but on some combination od display scales it can look ugly. If disabled, old UI will be used."),
app_config->get("sys_menu_enabled") == "1");
}
activate_options_tab(m_optgroup_dark_mode);
#endif //_WIN32
}
// update alignment of the controls for all tabs
update_ctrls_alignment();
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(tabs, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
this->Bind(wxEVT_BUTTON, &PreferencesDialog::accept, this, wxID_OK);
this->Bind(wxEVT_BUTTON, &PreferencesDialog::revert, this, wxID_CANCEL);
for (int id : {wxID_OK, wxID_CANCEL})
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowById(id, this)));
sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 10);
SetSizer(sizer);
sizer->SetSizeHints(this);
this->CenterOnParent();
}
std::vector<ConfigOptionsGroup*> PreferencesDialog::optgroups()
{
std::vector<ConfigOptionsGroup*> out;
out.reserve(4);
for (ConfigOptionsGroup* opt : { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get(), m_optgroup_other.get()
#ifdef _WIN32
, m_optgroup_dark_mode.get()
#endif // _WIN32
#if ENABLE_ENVIRONMENT_MAP
, m_optgroup_render.get()
#endif // ENABLE_ENVIRONMENT_MAP
})
if (opt)
out.emplace_back(opt);
return out;
}
void PreferencesDialog::update_ctrls_alignment()
{
int max_ctrl_width{ 0 };
for (ConfigOptionsGroup* og : this->optgroups())
if (int max = og->custom_ctrl->get_max_win_width();
max_ctrl_width < max)
max_ctrl_width = max;
if (max_ctrl_width)
for (ConfigOptionsGroup* og : this->optgroups())
og->custom_ctrl->set_max_win_width(max_ctrl_width);
}
void PreferencesDialog::accept(wxEvent&)
{
if (const auto it = m_values.find("downloader_url_registered"); it != m_values.end())
downloader->allow(it->second == "1");
if (!downloader->on_finish())
return;
#ifdef __linux__
if( downloader->get_perform_registration_linux())
DesktopIntegrationDialog::perform_desktop_integration(true);
#endif // __linux__
std::vector<std::string> options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" };
for (const std::string& option : options_to_recreate_GUI) {
if (m_values.find(option) != m_values.end()) {
wxString title = wxGetApp().is_editor() ? wxString(SLIC3R_APP_NAME) : wxString(GCODEVIEWER_APP_NAME);
title += " - " + _L("Changes for the critical options");
MessageDialog dialog(nullptr,
_L("Changing some options will trigger application restart.\n"
"You will lose the content of the plater.") + "\n\n" +
_L("Do you want to proceed?"),
title,
wxICON_QUESTION | wxYES | wxNO);
if (dialog.ShowModal() == wxID_YES) {
m_recreate_GUI = true;
}
else {
for (const std::string& option : options_to_recreate_GUI)
m_values.erase(option);
}
break;
}
}
auto app_config = get_app_config();
m_seq_top_layer_only_changed = false;
if (auto it = m_values.find("seq_top_layer_only"); it != m_values.end())
m_seq_top_layer_only_changed = app_config->get("seq_top_layer_only") != it->second;
m_settings_layout_changed = false;
for (const std::string& key : { "old_settings_layout_mode",
"new_settings_layout_mode",
"dlg_settings_layout_mode" })
{
auto it = m_values.find(key);
if (it != m_values.end() && app_config->get(key) != it->second) {
m_settings_layout_changed = true;
break;
}
}
#if 0 //#ifdef _WIN32 // #ysDarkMSW - Allow it when we deside to support the sustem colors for application
if (m_values.find("always_dark_color_mode") != m_values.end())
wxGetApp().force_sys_colors_update();
#endif
for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it)
app_config->set(it->first, it->second);
app_config->save();
if (wxGetApp().is_editor()) {
wxGetApp().set_label_clr_sys(m_sys_colour->GetColour());
wxGetApp().set_label_clr_modified(m_mod_colour->GetColour());
wxGetApp().set_mode_palette(m_mode_palette);
}
EndModal(wxID_OK);
#ifdef _WIN32
if (m_values.find("dark_color_mode") != m_values.end())
wxGetApp().force_colors_update();
#ifdef _MSW_DARK_MODE
if (m_values.find("sys_menu_enabled") != m_values.end())
wxGetApp().force_menu_update();
#endif //_MSW_DARK_MODE
#endif // _WIN32
wxGetApp().update_ui_from_settings();
clear_cache();
}
void PreferencesDialog::revert(wxEvent&)
{
auto app_config = get_app_config();
bool save_app_config = false;
if (m_custom_toolbar_size != atoi(app_config->get("custom_toolbar_size").c_str())) {
app_config->set("custom_toolbar_size", (boost::format("%d") % m_custom_toolbar_size).str());
m_icon_size_slider->SetValue(m_custom_toolbar_size);
save_app_config |= true;
}
if (m_use_custom_toolbar_size != (get_app_config()->get("use_custom_toolbar_size") == "1")) {
app_config->set("use_custom_toolbar_size", m_use_custom_toolbar_size ? "1" : "0");
save_app_config |= true;
m_optgroup_gui->set_value("use_custom_toolbar_size", m_use_custom_toolbar_size);
m_icon_size_sizer->ShowItems(m_use_custom_toolbar_size);
refresh_og(m_optgroup_gui);
}
if (save_app_config)
app_config->save();
for (auto value : m_values) {
const std::string& key = value.first;
if (key == "default_action_on_dirty_project") {
m_optgroup_general->set_value(key, app_config->get(key).empty());
continue;
}
if (key == "default_action_on_close_application" || key == "default_action_on_select_preset" || key == "default_action_on_new_project") {
m_optgroup_general->set_value(key, app_config->get(key) == "none");
continue;
}
if (key == "notify_release") {
m_optgroup_gui->set_value(key, s_keys_map_NotifyReleaseMode.at(app_config->get(key)));
continue;
}
if (key == "old_settings_layout_mode") {
m_rb_old_settings_layout_mode->SetValue(app_config->get(key) == "1");
m_settings_layout_changed = false;
continue;
}
if (key == "new_settings_layout_mode") {
m_rb_new_settings_layout_mode->SetValue(app_config->get(key) == "1");
m_settings_layout_changed = false;
continue;
}
if (key == "dlg_settings_layout_mode") {
m_rb_dlg_settings_layout_mode->SetValue(app_config->get(key) == "1");
m_settings_layout_changed = false;
continue;
}
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui, m_optgroup_other
#ifdef _WIN32
, m_optgroup_dark_mode
#endif // _WIN32
#if ENABLE_ENVIRONMENT_MAP
, m_optgroup_render
#endif // ENABLE_ENVIRONMENT_MAP
}) {
if (opt_group->set_value(key, app_config->get(key) == "1"))
break;
}
if (key == "tabs_as_menu") {
m_rb_new_settings_layout_mode->Show(app_config->get(key) != "1");
refresh_og(m_optgroup_gui);
continue;
}
}
clear_cache();
EndModal(wxID_CANCEL);
}
void PreferencesDialog::msw_rescale()
{
for (ConfigOptionsGroup* og : this->optgroups())
og->msw_rescale();
update_ctrls_alignment();
msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL });
layout();
}
void PreferencesDialog::on_sys_color_changed()
{
#ifdef _WIN32
wxGetApp().UpdateDlgDarkUI(this);
#endif
}
void PreferencesDialog::layout()
{
const int em = em_unit();
SetMinSize(wxSize(47 * em, 28 * em));
Fit();
Refresh();
}
void PreferencesDialog::clear_cache()
{
m_values.clear();
m_custom_toolbar_size = -1;
}
void PreferencesDialog::refresh_og(std::shared_ptr<ConfigOptionsGroup> og)
{
og->parent()->Layout();
tabs->Layout();
this->layout();
}
void PreferencesDialog::create_icon_size_slider()
{
const auto app_config = get_app_config();
const int em = em_unit();
m_icon_size_sizer = new wxBoxSizer(wxHORIZONTAL);
wxWindow* parent = m_optgroup_gui->parent();
wxGetApp().UpdateDarkUI(parent);
if (isOSX)
// For correct rendering of the slider and value label under OSX
// we should use system default background
parent->SetBackgroundStyle(wxBG_STYLE_ERASE);
auto label = new wxStaticText(parent, wxID_ANY, _L("Icon size in a respect to the default size") + " (%) :");
m_icon_size_sizer->Add(label, 0, wxALIGN_CENTER_VERTICAL| wxRIGHT | (isOSX ? 0 : wxLEFT), em);
const int def_val = atoi(app_config->get("custom_toolbar_size").c_str());
long style = wxSL_HORIZONTAL;
if (!isOSX)
style |= wxSL_LABELS | wxSL_AUTOTICKS;
m_icon_size_slider = new wxSlider(parent, wxID_ANY, def_val, 30, 100,
wxDefaultPosition, wxDefaultSize, style);
m_icon_size_slider->SetTickFreq(10);
m_icon_size_slider->SetPageSize(10);
m_icon_size_slider->SetToolTip(_L("Select toolbar icon size in respect to the default one."));
m_icon_size_sizer->Add(m_icon_size_slider, 1, wxEXPAND);
wxStaticText* val_label{ nullptr };
if (isOSX) {
val_label = new wxStaticText(parent, wxID_ANY, wxString::Format("%d", def_val));
m_icon_size_sizer->Add(val_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em);
}
m_icon_size_slider->Bind(wxEVT_SLIDER, ([this, val_label, app_config](wxCommandEvent e) {
auto val = m_icon_size_slider->GetValue();
app_config->set("custom_toolbar_size", (boost::format("%d") % val).str());
app_config->save();
wxGetApp().plater()->get_current_canvas3D()->render();
if (val_label)
val_label->SetLabelText(wxString::Format("%d", val));
}), m_icon_size_slider->GetId());
for (wxWindow* win : std::vector<wxWindow*>{ m_icon_size_slider, label, val_label }) {
if (!win) continue;
win->SetFont(wxGetApp().normal_font());
if (isOSX) continue; // under OSX we use wxBG_STYLE_ERASE
win->SetBackgroundStyle(wxBG_STYLE_PAINT);
}
m_optgroup_gui->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em);
}
void PreferencesDialog::create_settings_mode_widget()
{
wxWindow* parent = m_optgroup_gui->parent();
wxGetApp().UpdateDarkUI(parent);
wxString title = L("Layout Options");
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
wxGetApp().UpdateDarkUI(stb);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
stb->SetFont(wxGetApp().normal_font());
wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
auto app_config = get_app_config();
std::vector<wxString> choices = { _L("Old regular layout with the tab bar"),
_L("New layout, access via settings button in the top menu"),
_L("Settings in non-modal window") };
int id = -1;
auto add_radio = [this, parent, stb_sizer, choices](wxRadioButton** rb, int id, bool select) {
*rb = new wxRadioButton(parent, wxID_ANY, choices[id], wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
stb_sizer->Add(*rb);
(*rb)->SetValue(select);
(*rb)->Bind(wxEVT_RADIOBUTTON, [this, id](wxCommandEvent&) {
m_values["old_settings_layout_mode"] = (id == 0) ? "1" : "0";
m_values["new_settings_layout_mode"] = (id == 1) ? "1" : "0";
m_values["dlg_settings_layout_mode"] = (id == 2) ? "1" : "0";
});
};
add_radio(&m_rb_old_settings_layout_mode, ++id, app_config->get("old_settings_layout_mode") == "1");
add_radio(&m_rb_new_settings_layout_mode, ++id, app_config->get("new_settings_layout_mode") == "1");
add_radio(&m_rb_dlg_settings_layout_mode, ++id, app_config->get("dlg_settings_layout_mode") == "1");
#ifdef _MSW_DARK_MODE
if (app_config->get("tabs_as_menu") == "1") {
m_rb_new_settings_layout_mode->Hide();
if (m_rb_new_settings_layout_mode->GetValue()) {
m_rb_new_settings_layout_mode->SetValue(false);
m_rb_old_settings_layout_mode->SetValue(true);
}
}
#endif
std::string opt_key = "settings_layout_mode";
m_blinkers[opt_key] = new BlinkingBitmap(parent);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::create_settings_text_color_widget()
{
wxWindow* parent = m_optgroup_gui->parent();
wxString title = L("Text colors");
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
wxGetApp().UpdateDarkUI(stb);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
std::string opt_key = "text_colors";
m_blinkers[opt_key] = new BlinkingBitmap(parent);
wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
ButtonsDescription::FillSizerWithTextColorDescriptions(stb_sizer, parent, &m_sys_colour, &m_mod_colour);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::create_settings_mode_color_widget()
{
wxWindow* parent = m_optgroup_gui->parent();
wxString title = L("Mode markers");
wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title));
wxGetApp().UpdateDarkUI(stb);
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
std::string opt_key = "mode_markers";
m_blinkers[opt_key] = new BlinkingBitmap(parent);
wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxVERTICAL);
// Mode color markers description
m_mode_palette = wxGetApp().get_mode_palette();
ButtonsDescription::FillSizerWithModeColorDescriptions(stb_sizer, parent, { &m_mode_simple, &m_mode_advanced, &m_mode_expert }, m_mode_palette);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_gui->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
append_preferences_option_to_searcer(m_optgroup_gui, opt_key, title);
}
void PreferencesDialog::create_downloader_path_sizer()
{
wxWindow* parent = m_optgroup_other->parent();
wxString title = L("Download path");
std::string opt_key = "url_downloader_dest";
m_blinkers[opt_key] = new BlinkingBitmap(parent);
downloader = new DownloaderUtils::Worker(parent);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2);
sizer->Add(downloader, 1, wxALIGN_CENTER_VERTICAL);
m_optgroup_other->sizer->Add(sizer, 0, wxEXPAND | wxTOP, em_unit());
append_preferences_option_to_searcer(m_optgroup_other, opt_key, title);
}
void PreferencesDialog::init_highlighter(const t_config_option_key& opt_key)
{
if (m_blinkers.find(opt_key) != m_blinkers.end())
if (BlinkingBitmap* blinker = m_blinkers.at(opt_key); blinker) {
m_highlighter.init(blinker);
return;
}
for (auto opt_group : { m_optgroup_general, m_optgroup_camera, m_optgroup_gui, m_optgroup_other
#ifdef _WIN32
, m_optgroup_dark_mode
#endif // _WIN32
#if ENABLE_ENVIRONMENT_MAP
, m_optgroup_render
#endif // ENABLE_ENVIRONMENT_MAP
}) {
std::pair<OG_CustomCtrl*, bool*> ctrl = opt_group->get_custom_ctrl_with_blinking_ptr(opt_key, -1);
if (ctrl.first && ctrl.second) {
m_highlighter.init(ctrl);
break;
}
}
}
} // GUI
} // Slic3r