Merge remote-tracking branch 'origin/main' into feature/protobuf_config_and_dynamic_ui

Reconcile main's config changes with the protobuf codegen:
- New settings ported to proto schema (already present) and verified in
  generated PrintConfigDef/Preset_options/TabLayout + PrintConfig.hpp:
  small_support_perimeter_speed, small_support_perimeter_threshold,
  top_layer_direction, bottom_layer_direction, hole_to_polyhole_max_edges
- Ported main's label/tooltip updates for adaptive_pressure_advance_overhangs
  and adaptive_pressure_advance_bridges into filament.proto (were dropped in
  the conflict resolution) and regenerated.
- Kept main's non-generated logic (Preset.cpp nullable filament override
  force-emit).

Codegen validated (python tools/run_codegen.py --validate-only): PASSED.
This commit is contained in:
ExPikaPaka
2026-07-08 10:31:10 +02:00
244 changed files with 110208 additions and 14226 deletions

View File

@@ -85,6 +85,7 @@ def mode_to_cpp(mode_val):
meta_pb2.MODE_SIMPLE: "comSimple",
meta_pb2.MODE_ADVANCED: "comAdvanced",
meta_pb2.MODE_DEVELOP: "comDevelop",
meta_pb2.MODE_EXPERT: "comExpert",
}.get(mode_val, "comAdvanced")
@@ -128,7 +129,8 @@ def proto_type_to_co_type(field_desc, is_nullable=False):
if ftype == TYPE_MESSAGE:
if "FloatOrPercent" in type_name:
if is_repeated:
return ("coFloatsOrPercents", "ConfigOptionFloatsOrPercents", True)
cls = "ConfigOptionFloatsOrPercentsNullable" if is_nullable else "ConfigOptionFloatsOrPercents"
return ("coFloatsOrPercents", cls, True)
return ("coFloatOrPercent", "ConfigOptionFloatOrPercent", False)
elif "Point2D" in type_name:
if is_repeated:
@@ -518,10 +520,11 @@ class CodeGenerator:
cls = "ConfigOptionEnumsGenericNullable" if is_nullable else "ConfigOptionEnumsGeneric"
return f"new {cls}{{}}"
NULLABLE_LIST_CLASS = {
"coFloats": "ConfigOptionFloatsNullable",
"coInts": "ConfigOptionIntsNullable",
"coBools": "ConfigOptionBoolsNullable",
"coPercents": "ConfigOptionPercentsNullable",
"coFloats": "ConfigOptionFloatsNullable",
"coInts": "ConfigOptionIntsNullable",
"coBools": "ConfigOptionBoolsNullable",
"coPercents": "ConfigOptionPercentsNullable",
"coFloatsOrPercents": "ConfigOptionFloatsOrPercentsNullable",
}
all_classes = {**SCALAR_CLASS, **LIST_CLASS}
if is_nullable and co_type in NULLABLE_LIST_CLASS:
@@ -533,6 +536,12 @@ class CodeGenerator:
if co_type in SCALAR_CLASS:
return f"new {SCALAR_CLASS[co_type]}({args})"
if co_type == "coFloatsOrPercents":
# Vector default is a single FloatOrPercent element (broadcast across
# extruders for nullable per-extruder configs). args e.g. "0, false".
cls = "ConfigOptionFloatsOrPercentsNullable" if is_nullable else "ConfigOptionFloatsOrPercents"
return f"new {cls}{{FloatOrPercent({args})}}"
if co_type in LIST_CLASS:
NULLABLE_LIST_CLASS = {
"coFloats": "ConfigOptionFloatsNullable",
@@ -736,7 +745,8 @@ def _extract_field_paths(tab_layout_cpp):
return mapping
def generate_tab_layout(layout_yaml_path, output_path, existing_cpp_path=None):
def generate_tab_layout(layout_yaml_path, output_path, existing_cpp_path=None,
variant_fields=None):
"""
Generate TabLayout_generated.cpp from layout.yaml.
@@ -763,6 +773,15 @@ def generate_tab_layout(layout_yaml_path, output_path, existing_cpp_path=None):
# Bootstrap: extract existing field→path mappings so yaml doesn't need them all
path_map = _extract_field_paths(existing_cpp_path) if existing_cpp_path else {}
variant_fields = variant_fields or set()
def _line_args(key, path):
"""Build the trailing args for append_single_option_line(key, path, idx).
Emits the variant opt index (0) for per-extruder multi-variant fields."""
if key in variant_fields:
return f', "{path}", 0' # path may be empty; idx=0 marks variant-aware
return f', "{path}"' if path else ''
lines = [
"// ===== AUTO-GENERATED by tools/config_codegen.py from layout.yaml =====",
"// DO NOT EDIT MANUALLY. Edit layout.yaml and re-run: python tools/run_codegen.py",
@@ -838,12 +857,15 @@ def generate_tab_layout(layout_yaml_path, output_path, existing_cpp_path=None):
# Regular group: generate append_single_option_line / multi-option line
for field in fields:
if isinstance(field, list):
# Multi-option line: [key1, key2, ...]
# Multi-option line: [key1, key2, ...]. Variant fields
# take the opt index via get_option(key, 0).
lines.append(" {")
first = field[0]
lines.append(f" Line line_{{optgroup->get_option(\"{first}\").opt.label, optgroup->get_option(\"{first}\").opt.tooltip}};")
first_idx = ", 0" if first in variant_fields else ""
lines.append(f" Line line_{{optgroup->get_option(\"{first}\"{first_idx}).opt.label, optgroup->get_option(\"{first}\"{first_idx}).opt.tooltip}};")
for k in field:
lines.append(f" line_.append_option(optgroup->get_option(\"{k}\"));")
k_idx = ", 0" if k in variant_fields else ""
lines.append(f" line_.append_option(optgroup->get_option(\"{k}\"{k_idx}));")
lines.append(" optgroup->append_line(line_);")
lines.append(" }")
elif isinstance(field, str):
@@ -851,13 +873,11 @@ def generate_tab_layout(layout_yaml_path, output_path, existing_cpp_path=None):
lines.append(" optgroup->append_separator();")
else:
path = path_map.get(field, '')
path_arg = f', "{path}"' if path else ''
lines.append(f" optgroup->append_single_option_line(\"{field}\"{path_arg});")
lines.append(f" optgroup->append_single_option_line(\"{field}\"{_line_args(field, path)});")
elif isinstance(field, dict):
# {key: path} explicit path
for key, path in field.items():
path_arg = f', "{path}"' if path else ''
lines.append(f" optgroup->append_single_option_line(\"{key}\"{path_arg});")
lines.append(f" optgroup->append_single_option_line(\"{key}\"{_line_args(key, path)});")
lines.append(" }")
@@ -1016,7 +1036,14 @@ def main():
if layout_yaml.exists():
tab_layout_out = output_dir / "TabLayout_generated.cpp"
existing = tab_layout_out if tab_layout_out.exists() else None
generate_tab_layout(str(layout_yaml), str(tab_layout_out), str(existing) if existing else None)
# Per-extruder multi-variant fields = nullable PRESET_PRINT settings.
# These get the variant opt index so the generated layout binds them to
# the extruder/variant switch (filament nullable overrides use a separate
# checkbox mechanism and must NOT get an index).
variant_fields = {f.name for f in gen.fields
if f.is_nullable and f.preset == meta_pb2.PRESET_PRINT}
generate_tab_layout(str(layout_yaml), str(tab_layout_out),
str(existing) if existing else None, variant_fields)
else:
print(f" NOTE: layout.yaml not found at {layout_yaml}, skipping tab layout generation")