Ajoute une propriété "Ombre portée" (box-shadow), disponible sur tout widget
Nouveau groupe "Ombre" dans le panneau de propriétés, à côté de "Bordure" (même universalité — voir UNIVERSAL_CONTROLS) : décalage X/Y, flou, étendue, couleur et opacité, combinés en une seule valeur CSS box-shadow (couleur+opacité fusionnées en rgba(), un <input type="color"> seul ne portant pas de canal alpha). Décalages/flou/étendue tous à 0 = pas d'ombre, même convention que border-width à 0 = pas de bordure. Nouveau ctype "shadow" (c_shadow.py), suit exactement le même principe que le ctype "size" déjà existant (size_override_controls.py) : plusieurs entrées de formulaire pour un seul réglage, composées/décomposées dans save_element_controls.py et control_value.py plutôt que passées par le chemin générique clé->valeur. Ajoute tests/test_shadow_controls.py (présence dans le panneau, composition de la valeur CSS, aller-retour dans le formulaire, remise à zéro qui retire l'ombre). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9c9e3c5379
commit
bda082bffd
@@ -6,6 +6,16 @@ from ..widgets.widget_meta import widget_meta
|
||||
from .get_element import get_element
|
||||
|
||||
|
||||
def _hex_to_rgb(hex_color):
|
||||
h = (hex_color or "").lstrip("#")
|
||||
if len(h) != 6:
|
||||
return (0, 0, 0)
|
||||
try:
|
||||
return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
|
||||
except ValueError:
|
||||
return (0, 0, 0)
|
||||
|
||||
|
||||
def _is_default_value(control, raw_value):
|
||||
"""Compare la valeur reçue du formulaire à la valeur par défaut du
|
||||
réglage (en tenant compte de l'unité ajoutée pour les sliders), pour
|
||||
@@ -83,6 +93,37 @@ def save_element_controls(slug, element_id, form):
|
||||
style[name] = f"{num_str}{unit}"
|
||||
continue
|
||||
|
||||
if ctype == "shadow":
|
||||
# 6 entrées de formulaire (décalage X/Y, flou, étendue, couleur,
|
||||
# opacité — voir c_shadow.py) combinées en une seule valeur CSS
|
||||
# "box-shadow" (couleur+opacité fusionnées en rgba(), un
|
||||
# <input type="color"> seul ne portant pas de canal alpha).
|
||||
_kind, name = control["target"].split(":", 1)
|
||||
defaults = control.get("default", {})
|
||||
|
||||
def _num(suffix, fallback):
|
||||
try:
|
||||
return float(form.get(field_name + suffix, fallback))
|
||||
except (TypeError, ValueError):
|
||||
return fallback
|
||||
|
||||
x = _num("_x", defaults.get("x", 0))
|
||||
y = _num("_y", defaults.get("y", 4))
|
||||
blur = _num("_blur", defaults.get("blur", 12))
|
||||
spread = _num("_spread", defaults.get("spread", 0))
|
||||
opacity = _num("_opacity", defaults.get("opacity", 0.4))
|
||||
color = form.get(field_name + "_color") or defaults.get("color", "#000000")
|
||||
if x == 0 and y == 0 and blur == 0 and spread == 0:
|
||||
style.pop(name, None)
|
||||
else:
|
||||
r, g, b = _hex_to_rgb(color)
|
||||
|
||||
def _px(n):
|
||||
return str(int(n)) if n == int(n) else str(n)
|
||||
|
||||
style[name] = f"{_px(x)}px {_px(y)}px {_px(blur)}px {_px(spread)}px rgba({r}, {g}, {b}, {opacity})"
|
||||
continue
|
||||
|
||||
target = control["target"]
|
||||
if ctype == "checkbox":
|
||||
checked = form.get(field_name) is not None
|
||||
|
||||
Reference in New Issue
Block a user