Fix: unticking "Rotate for tape feed" had no effect

An unticked checkbox is absent from a form submission entirely, so
"rotate unticked" and "rotate never mentioned" arrived at the parser
looking identical - and rotate defaults to on, so it snapped straight
back every time. Add a hidden marker the options form always sends, so
absent-but-submitted can be read as unticked.

Same bug silently reset the field list when every field was unticked;
that is fixed by the same marker. rotate_default lets a saved site-wide
preference apply on a fresh arrival without overriding an explicit
untick on the page.
This commit is contained in:
jessikitty committed 2026-10-12 22:08:51 +11:00
1 parent bad4787e00
commit 4a1bfc4acd
1 file changed
+40 -14
+40 -14
View File
@@ -14,9 +14,9 @@ VC-500W facts this is built around:
ORIENTATION: the tape feeds lengthwise, so the raster sent to the printer is ORIENTATION: the tape feeds lengthwise, so the raster sent to the printer is
normally rotated 90 degrees - the image is tape-width across and normally rotated 90 degrees - the image is tape-width across and
label-length tall. That is the default. If your first test print comes out label-length tall. That is the default. If your prints come out sideways,
sideways, untick "Rotate for tape feed" and print again; it is the one untick "Rotate for tape feed"; to make that permanent, turn off the default
setting worth checking on a new printer. orientation in Label Settings.
Created by: Jess Rogerson (yelling commands at Claude.AI) Created by: Jess Rogerson (yelling commands at Claude.AI)
""" """
@@ -73,6 +73,16 @@ TAPE_PRESETS = {
DEFAULT_TAPE = 'cz25' DEFAULT_TAPE = 'cz25'
# What a label shows when you have not said otherwise. Defined once: the
# dataclass default and the form parser both read it, so they cannot drift.
DEFAULT_TAPE_FIELDS = ['image', 'name', 'dimensions', 'part_number', 'accent']
# Hidden marker every options form submits. Its presence is how we tell
# "submitted with the box unticked" from "arrived here with no options at
# all" - an unticked checkbox sends nothing, so without this they look
# identical and a default-on box can never be turned off.
FORM_MARKER = '_opts'
FONT_CANDIDATES = { FONT_CANDIDATES = {
False: [ # regular False: [ # regular
'C:\\Windows\\Fonts\\segoeui.ttf', 'C:\\Windows\\Fonts\\segoeui.ttf',
@@ -114,8 +124,8 @@ def load_font(size_px, bold=False):
@dataclass @dataclass
class TapeOptions: class TapeOptions:
tape: str = DEFAULT_TAPE tape: str = DEFAULT_TAPE
fields: List[str] = field(default_factory=lambda: [ fields: List[str] = field(
'image', 'name', 'dimensions', 'part_number', 'accent']) default_factory=lambda: list(DEFAULT_TAPE_FIELDS))
rotate: bool = True rotate: bool = True
copies: int = 1 copies: int = 1
# Fixed length in mm, or None to size each label to its content. # Fixed length in mm, or None to size each label to its content.
@@ -131,8 +141,21 @@ def get_tape(key):
return TAPE_PRESETS.get(key or DEFAULT_TAPE, TAPE_PRESETS[DEFAULT_TAPE]) return TAPE_PRESETS.get(key or DEFAULT_TAPE, TAPE_PRESETS[DEFAULT_TAPE])
def options_from_form(form): def options_from_form(form, rotate_default=True):
"""Build TapeOptions from request.form / request.args.""" """
Build TapeOptions from request.form / request.args.
An unticked checkbox is simply absent from a form submission - the
browser sends nothing for it. So "rotate unticked" and "rotate never
mentioned" arrive here looking exactly the same, and a box that
defaults to on could never be turned off. FORM_MARKER is a hidden
field the options form always sends: when it is present we know every
checkbox was on screen, so absent means the user unticked it.
rotate_default applies only when the form was NOT submitted (a fresh
arrival), so the saved site-wide preference is honoured there.
"""
submitted = FORM_MARKER in form
def number(key, default, cast=float): def number(key, default, cast=float):
try: try:
@@ -141,11 +164,15 @@ def options_from_form(form):
return default return default
def flag(key, default=False): def flag(key, default=False):
if key not in form: if key in form:
return default return str(form.get(key, '')).lower() in ('1', 'true', 'on', 'yes')
return str(form.get(key, '')).lower() in ('1', 'true', 'on', 'yes') # Submitted without it: the user unticked it. Not submitted: default.
return False if submitted else default
fields = list(form.getlist('fields')) if hasattr(form, 'getlist') else []
if not fields and not submitted:
fields = list(DEFAULT_TAPE_FIELDS)
fields = form.getlist('fields') if hasattr(form, 'getlist') else None
length = (form.get('fixed_length_mm') or '').strip() if hasattr(form, 'get') else '' length = (form.get('fixed_length_mm') or '').strip() if hasattr(form, 'get') else ''
try: try:
fixed = float(length) if length else None fixed = float(length) if length else None
@@ -154,9 +181,8 @@ def options_from_form(form):
return TapeOptions( return TapeOptions(
tape=form.get('tape') or DEFAULT_TAPE, tape=form.get('tape') or DEFAULT_TAPE,
fields=fields if fields else ['image', 'name', 'dimensions', fields=fields,
'part_number', 'accent'], rotate=flag('rotate', rotate_default),
rotate=flag('rotate', True),
copies=max(1, min(99, number('copies', 1, int))), copies=max(1, min(99, number('copies', 1, int))),
fixed_length_mm=fixed, fixed_length_mm=fixed,
padding_mm=max(0.0, min(10.0, number('padding_mm', 1.5))), padding_mm=max(0.0, min(10.0, number('padding_mm', 1.5))),