From 552736ec479903edbc4576182f52469c6dc222c5 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 20 Jul 2026 23:43:11 -0500 Subject: fix(design): clamp the gauge once so needle and readout agree The needle angle clamped to 0-100 while the readout rendered the raw value, so a gauge fed 150 pinned the needle at +60 degrees under a "150%" label. A shared gallery-widget--clamp-value now feeds both halves. Two hygiene fixes ride along. The module requires cl-lib explicitly instead of leaning on svg.el's transitive require. The token-file load resolves its directory through gallery-widget--source-dir, which falls back to default-directory when the form is re-evaluated outside a load and outside a file buffer (both location variables are nil there and file-name-directory errored). --- docs/prototypes/gallery-widget.el | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'docs/prototypes') diff --git a/docs/prototypes/gallery-widget.el b/docs/prototypes/gallery-widget.el index 5d69148..5a80e59 100644 --- a/docs/prototypes/gallery-widget.el +++ b/docs/prototypes/gallery-widget.el @@ -24,9 +24,16 @@ (require 'svg) (require 'dom) +(require 'cl-lib) -(load (expand-file-name "gallery-tokens.el" - (file-name-directory (or load-file-name buffer-file-name))) +(defun gallery-widget--source-dir () + "Directory holding this module's source (and gallery-tokens.el). +Falls back to `default-directory' when evaluated outside a load and +outside a file buffer, where both location variables are nil." + (let ((src (or load-file-name buffer-file-name))) + (if src (file-name-directory src) default-directory))) + +(load (expand-file-name "gallery-tokens.el" (gallery-widget--source-dir)) nil t) (defun gallery-widget-token (name) @@ -43,11 +50,17 @@ a typo'd token must fail loudly, not render black." (defconst gallery-widget--gauge-max-deg 60.0 "Needle angle at value 100.") -(defun gallery-widget--needle-angle (value) - "Map VALUE (0-100, clamped) onto the gauge's needle angle in degrees." +(defun gallery-widget--clamp-value (value) + "Validate VALUE is a number and clamp it to the gauge's 0-100 range. +One clamp shared by the needle angle and the readout, so the two halves +of the widget can never disagree at an out-of-range input." (unless (numberp value) (error "Gauge value must be a number, got %S" value)) - (let ((v (min 100.0 (max 0.0 (float value))))) + (min 100.0 (max 0.0 (float value)))) + +(defun gallery-widget--needle-angle (value) + "Map VALUE (0-100, clamped) onto the gauge's needle angle in degrees." + (let ((v (gallery-widget--clamp-value value))) (+ gallery-widget--gauge-min-deg (* (/ v 100.0) (- gallery-widget--gauge-max-deg gallery-widget--gauge-min-deg))))) @@ -77,6 +90,8 @@ Positive angles swing right, matching the gauge's needle travel." Geometry mirrors the web card: 96px-wide semicircular dial, pivot at its bottom center, 40px needle sweeping -60..+60 degrees, readout below." (let* ((w 96) (h 64) (cx 48.0) (cy 48.0) + ;; clamp once; the angle and the readout render the same value + (value (gallery-widget--clamp-value value)) (angle (gallery-widget--needle-angle value)) (tip (gallery-widget--polar cx cy 40.0 angle)) (svg (svg-create w h :viewBox (format "0 0 %d %d" w h)))) -- cgit v1.2.3