mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 01:02:08 +00:00
Fix plugin configuration not taking effect, and improve the plugin config UI (#14944)
# Description Changing a slicing plugin's configuration had no effect on the sliced result until you forced a re-slice some other way; it now applies immediately. Print, printer and filament presets also keep their plugin configuration separately, so configuring a plugin on one no longer wipes out what you set on another. A plugin's custom configuration page gets the same round of improvements in both the Plugins dialog and the per-preset dialog: it follows the app's light/dark theme, keeps its state while you edit instead of resetting under the cursor, and can tell whether it is being edited globally or for a preset, so "Restore defaults" can be labeled for what it will actually do. The two bundled examples show this off — Twistify now ships a custom configuration UI, and Inspector is themed, groups # Screenshots/Recordings/Graphs https://github.com/user-attachments/assets/02ca062a-5143-49a3-abe0-a2a040b3a928 ## Tests <!-- > Please describe the tests that you have conducted to verify the changes made in this PR. --> <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
This commit is contained in:
@@ -1203,7 +1203,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"post_process",
|
||||
"slicing_pipeline_plugin",
|
||||
"plugins",
|
||||
"plugin_config_overrides",
|
||||
"print_plugin_config_overrides",
|
||||
"process_change_extrusion_role_gcode",
|
||||
"min_length_factor",
|
||||
"wall_maximum_resolution",
|
||||
@@ -1378,7 +1378,7 @@ static std::vector<std::string> s_Preset_filament_options {/*"filament_colour",
|
||||
"filament_preheat_temperature_delta", "filament_retract_length_nc",
|
||||
"filament_change_length_nc", "filament_prime_volume", "filament_prime_volume_nc",
|
||||
"long_retractions_when_ec", "retraction_distances_when_ec",
|
||||
"plugin_config_overrides",
|
||||
"filament_plugin_config_overrides",
|
||||
//ams chamber
|
||||
"filament_dev_ams_drying_ams_limitations", "filament_dev_ams_drying_temperature", "filament_dev_ams_drying_time", "filament_dev_ams_drying_heat_distortion_temperature",
|
||||
"filament_dev_chamber_drying_bed_temperature", "filament_dev_chamber_drying_time",
|
||||
@@ -1430,7 +1430,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
// Fast-purge printer flag + device/firmware-facing per-variant extruder-change
|
||||
// deretraction speed (unconsumed by the slicer; carried by H2D/A2L/X2D/P2S machine profiles).
|
||||
"support_fast_purge_mode", "deretract_speed_extruder_change",
|
||||
"plugin_config_overrides"
|
||||
"printer_plugin_config_overrides"
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_sla_print_options {
|
||||
@@ -1542,6 +1542,15 @@ const std::vector<std::string>& Preset::printer_options()
|
||||
return s_opts;
|
||||
}
|
||||
|
||||
const char* Preset::plugin_overrides_key(Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case TYPE_PRINTER: return "printer_plugin_config_overrides";
|
||||
case TYPE_FILAMENT: return "filament_plugin_config_overrides";
|
||||
default: return "print_plugin_config_overrides";
|
||||
}
|
||||
}
|
||||
|
||||
PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) :
|
||||
m_type(type),
|
||||
m_edited_preset(type, "", false),
|
||||
|
||||
@@ -407,6 +407,11 @@ public:
|
||||
// Printer machine limits, those are contained in printer_options().
|
||||
static const std::vector<std::string>& machine_limits_options();
|
||||
|
||||
// Option key holding this preset type's plugin capability overrides. Each type has its own key so
|
||||
// the values survive the merge into a single full config; print is the fallback for the types with
|
||||
// no plugin-backed options.
|
||||
static const char* plugin_overrides_key(Type type);
|
||||
|
||||
static const std::vector<std::string>& sla_printer_options();
|
||||
static const std::vector<std::string>& sla_material_options();
|
||||
static const std::vector<std::string>& sla_print_options();
|
||||
|
||||
@@ -285,6 +285,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
opt_key == "slicing_pipeline_plugin"
|
||||
|| opt_key == "print_plugin_config_overrides"
|
||||
|| opt_key == "initial_layer_print_height"
|
||||
|| opt_key == "nozzle_diameter"
|
||||
|| opt_key == "filament_shrink"
|
||||
|
||||
@@ -1081,16 +1081,21 @@ void PrintConfigDef::init_common_params()
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
}
|
||||
|
||||
def = this->add("plugin_config_overrides", coString);
|
||||
def->label = L("Capabilities");
|
||||
def->tooltip = L("Configuration for the plugin capabilities this preset uses, overriding the global "
|
||||
"Capabilities configuration. Stored as a raw JSON array and edited through the dialog "
|
||||
"behind the button, never typed in directly.");
|
||||
// Never shown as a text field: GUIType::plugin_config renders a button that opens PluginsConfigDialog.
|
||||
def->gui_type = ConfigOptionDef::GUIType::plugin_config;
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
// One key per preset type (Preset::plugin_overrides_key), so the print, printer and filament
|
||||
// overrides don't clobber each other when the presets merge into one full config. No handle_legacy
|
||||
// migration from the shared "plugin_config_overrides" they replace: it only ever shipped in
|
||||
// nightlies. Never a text field — GUIType::plugin_config renders a button opening PluginsConfigDialog.
|
||||
for (const char* key : {"print_plugin_config_overrides", "printer_plugin_config_overrides", "filament_plugin_config_overrides"}) {
|
||||
def = this->add(key, coString);
|
||||
def->label = L("Capabilities");
|
||||
def->tooltip = L("Configuration for the plugin capabilities this preset uses, overriding the global "
|
||||
"Capabilities configuration. Stored as a raw JSON array and edited through the dialog "
|
||||
"behind the button, never typed in directly.");
|
||||
def->gui_type = ConfigOptionDef::GUIType::plugin_config;
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_fff_params()
|
||||
|
||||
@@ -1780,6 +1780,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionString, filename_format))
|
||||
((ConfigOptionStrings, post_process))
|
||||
((ConfigOptionStrings, slicing_pipeline_plugin))
|
||||
((ConfigOptionString, print_plugin_config_overrides))
|
||||
((ConfigOptionString, printer_model))
|
||||
((ConfigOptionFloat, resolution))
|
||||
((ConfigOptionFloats, retraction_minimum_travel))
|
||||
|
||||
@@ -1796,7 +1796,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
|
||||
// Keep this preset's "plugins" manifest in sync when a plugin picker changes, so full_config() and
|
||||
// save_to_json() always find resolved "name;uuid;capability" references and rebuild it nowhere else.
|
||||
// Also drop any plugin_config_overrides entries for a capability the change just stopped
|
||||
// Also drop any plugin config override entries for a capability the change just stopped
|
||||
// referencing (e.g. a plugin removed from slicing_pipeline_plugin), so a saved preset never
|
||||
// carries configuration for a capability it no longer names. The Configure button is a separate
|
||||
// field holding its own cached copy of that value, so it needs to be told explicitly, or it
|
||||
@@ -1804,9 +1804,10 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
if (const ConfigOptionDef* opt_def = m_config->def()->get(opt_key);
|
||||
opt_def && opt_def->is_plugin_backed()) {
|
||||
m_config->update_plugin_manifest();
|
||||
if (prune_stale_plugin_overrides(*m_config)) {
|
||||
if (Field* overrides_field = get_field(PLUGIN_OVERRIDES_OPTION_KEY))
|
||||
overrides_field->set_value(boost::any(m_config->opt_string(PLUGIN_OVERRIDES_OPTION_KEY)), false);
|
||||
const std::string overrides_key = Preset::plugin_overrides_key(m_type);
|
||||
if (prune_stale_plugin_overrides(*m_config, overrides_key)) {
|
||||
if (Field* overrides_field = get_field(overrides_key))
|
||||
overrides_field->set_value(boost::any(m_config->opt_string(overrides_key)), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3136,7 +3137,7 @@ void TabPrint::build()
|
||||
// Its own group: the one above hides its labels, and this row needs its label — and the revert
|
||||
// arrow beside it — to show. No label-width override either, as a 0 there means "no label column".
|
||||
optgroup = page->new_optgroup(L("Plugin Configuration"), L"param_gcode");
|
||||
optgroup->append_single_option_line("plugin_config_overrides");
|
||||
optgroup->append_single_option_line("print_plugin_config_overrides");
|
||||
|
||||
optgroup = page->new_optgroup(L("Notes"), "note", 0);
|
||||
option = optgroup->get_option("notes");
|
||||
@@ -4552,7 +4553,7 @@ void TabFilament::build()
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
optgroup = page->new_optgroup(L("Plugin Configuration"), L"param_gcode");
|
||||
optgroup->append_single_option_line("plugin_config_overrides");
|
||||
optgroup->append_single_option_line("filament_plugin_config_overrides");
|
||||
|
||||
page = add_options_page(L("Multimaterial"), "custom-gcode_multi_material"); // ORCA: icon only visible on placeholders
|
||||
optgroup = page->new_optgroup(L("Wipe tower parameters"), "param_tower");
|
||||
@@ -5061,7 +5062,7 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("time_cost", "printer_basic_information_advanced#time-cost");
|
||||
|
||||
optgroup = page->new_optgroup(L("Plugin Configuration"), L"param_gcode");
|
||||
optgroup->append_single_option_line("plugin_config_overrides");
|
||||
optgroup->append_single_option_line("printer_plugin_config_overrides");
|
||||
|
||||
optgroup = page->new_optgroup(L("Cooling Fan"), "param_cooling_fan");
|
||||
Line line = Line{ L("Fan speed-up time"), optgroup->get_option("fan_speedup_time").opt.tooltip };
|
||||
|
||||
@@ -324,12 +324,6 @@ bool PluginConfig::dirty() const
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
std::string plugin_overrides_of(const Preset& preset)
|
||||
{
|
||||
const auto* opt = dynamic_cast<const ConfigOptionString*>(preset.config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||
return opt == nullptr ? std::string() : opt->value;
|
||||
}
|
||||
|
||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error)
|
||||
{
|
||||
document = CapabilityConfigDocument();
|
||||
@@ -357,9 +351,9 @@ std::string serialize_plugin_overrides(const CapabilityConfigDocument& document)
|
||||
return document.empty() ? std::string() : document.serialize_entries().dump();
|
||||
}
|
||||
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config)
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config, const std::string& overrides_key)
|
||||
{
|
||||
const auto* overrides_opt = dynamic_cast<const ConfigOptionString*>(config.option(PLUGIN_OVERRIDES_OPTION_KEY));
|
||||
const auto* overrides_opt = dynamic_cast<const ConfigOptionString*>(config.option(overrides_key));
|
||||
if (overrides_opt == nullptr || overrides_opt->value.empty())
|
||||
return false;
|
||||
|
||||
@@ -397,7 +391,7 @@ bool prune_stale_plugin_overrides(DynamicConfig& config)
|
||||
if (!overrides.prune_unreferenced(referenced))
|
||||
return false;
|
||||
|
||||
config.set_key_value(PLUGIN_OVERRIDES_OPTION_KEY, new ConfigOptionString(serialize_plugin_overrides(overrides)));
|
||||
config.set_key_value(overrides_key, new ConfigOptionString(serialize_plugin_overrides(overrides)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -470,27 +464,29 @@ EffectiveCapabilityConfig active_capability_config(const PluginCapabilityId& id)
|
||||
|
||||
if (bundle != nullptr) {
|
||||
const std::string type_key = plugin_capability_type_to_string(id.type);
|
||||
// The edited preset of each type that can hold plugin-backed options, keyed by its option list.
|
||||
const std::pair<const std::vector<std::string>*, const Preset*> scopes[] = {
|
||||
{&Preset::print_options(), &bundle->prints.get_edited_preset()},
|
||||
{&Preset::printer_options(), &bundle->printers.get_edited_preset()},
|
||||
{&Preset::filament_options(), &bundle->filaments.get_edited_preset()},
|
||||
};
|
||||
for (const auto& [key, def] : print_config_def.options) {
|
||||
if (def.plugin_type != type_key)
|
||||
continue;
|
||||
|
||||
const auto& print_options = Preset::print_options();
|
||||
if (std::find(print_options.begin(), print_options.end(), key) != print_options.end()) {
|
||||
preset = &bundle->prints.get_edited_preset();
|
||||
for (const auto& [options, edited] : scopes)
|
||||
if (contains(*options, key)) {
|
||||
preset = edited;
|
||||
break;
|
||||
}
|
||||
if (preset != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& printer_options = Preset::printer_options();
|
||||
if (std::find(printer_options.begin(), printer_options.end(), key) != printer_options.end()) {
|
||||
preset = &bundle->printers.get_edited_preset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preset != nullptr) {
|
||||
const auto* stored = dynamic_cast<const ConfigOptionString*>(preset->config.option(Preset::plugin_overrides_key(preset->type)));
|
||||
std::string error;
|
||||
if (!parse_plugin_overrides(plugin_overrides_of(*preset), overrides, error)) {
|
||||
if (!parse_plugin_overrides(stored == nullptr ? std::string() : stored->value, overrides, error)) {
|
||||
// Text we cannot read is not an override: log it and resolve against the base config.
|
||||
BOOST_LOG_TRIVIAL(error) << "Preset \"" << preset->name << "\": " << error;
|
||||
overrides = CapabilityConfigDocument();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Preset;
|
||||
class DynamicConfig;
|
||||
struct CapabilityConfigEntry
|
||||
{
|
||||
@@ -50,19 +49,15 @@ private:
|
||||
std::vector<nlohmann::json> m_opaque_entries;
|
||||
};
|
||||
|
||||
inline constexpr const char* PLUGIN_OVERRIDES_OPTION_KEY = "plugin_config_overrides";
|
||||
|
||||
std::string plugin_overrides_of(const Preset& preset);
|
||||
bool parse_plugin_overrides(const std::string& raw, CapabilityConfigDocument& document, std::string& error);
|
||||
std::string serialize_plugin_overrides(const CapabilityConfigDocument& document);
|
||||
|
||||
// Drops plugin_config_overrides entries for capabilities no longer named by any plugin-backed
|
||||
// option's current value in `config` (e.g. slicing_pipeline_plugin cleared or switched to a
|
||||
// different capability), and writes the result back if anything changed. Called wherever a
|
||||
// plugin-backed option's value changes, so a saved preset never carries configuration for a
|
||||
// capability it no longer references. Returns true if `config` was modified, so a caller holding a
|
||||
// GUI field over PLUGIN_OVERRIDES_OPTION_KEY knows it must refresh that field's displayed value.
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config);
|
||||
// Drops override entries for capabilities no longer named by any plugin-backed option in `config`
|
||||
// (e.g. slicing_pipeline_plugin cleared or pointed at another capability) and writes the result back
|
||||
// to `overrides_key`. Call it wherever such an option changes, so a saved preset never carries
|
||||
// configuration for a capability it no longer references. Returns true if `config` was modified, so a
|
||||
// caller holding a GUI field over `overrides_key` knows to refresh it.
|
||||
bool prune_stale_plugin_overrides(DynamicConfig& config, const std::string& overrides_key);
|
||||
|
||||
struct EffectiveCapabilityConfig
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user