Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to EigenScript are documented here.
## [Unreleased]

### Added
- **lib/ui: per-widget button colors + hover/pressed shades (#566,
first slice of #594).** `_render_button` honors optional `bg` /
`text_color` fields on the button dict with theme fallback (the
toggle_button/label pattern), so semantic coloring — the record-red /
play-green transport buttons DeslanStudio had to drop (F-DS-12) — now
works. Custom-colored buttons get hover/pressed feedback as new theme
`hover_shade` / `pressed_shade` alpha overlays (a theme-color swap
would erase the custom color); theme-colored buttons keep the exact
`btn_hover`/`btn_pressed` swap, so existing apps render unchanged.
Custom theme dicts without the new keys degrade gracefully (no
shade). Docs: `gfx_rrect` and `gfx_clip` were registered but missing
from BUILTINS.md (the #393 undiscoverability class) — rows added.
- **gfx: proportional antialiased text via SDL2_ttf (#593).** The
DeslanStudio side-by-side against its Qt prototype showed feature
parity reading a decade old, and the dominant gap was the 5x7 bitmap
Expand Down
2 changes: 2 additions & 0 deletions docs/BUILTINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ libSDL2 at runtime — no SDL2 headers needed at build time.
| `gfx_line` | `gfx_line of [x1, y1, x2, y2, r, g, b]` | Line segment |
| `gfx_point` | `gfx_point of [x, y, r, g, b]` | Single pixel |
| `gfx_circle` | `gfx_circle of [cx, cy, radius, r, g, b]` | Filled circle (midpoint) |
| `gfx_rrect` | `gfx_rrect of [x, y, w, h, radius, r, g, b]` or `[..., a]` | Filled rounded rectangle (scanline corner fill); radius clamps to half the smaller dimension, `radius 0` = plain rect |
| `gfx_clip` | `gfx_clip of [x, y, w, h]` / `gfx_clip of null` | Set / clear the render clip rectangle |
| `gfx_text` | `gfx_text of [x, y, text, r, g, b]` or `[..., scale]` | Text. Proportional antialiased TTF when libSDL2_ttf + a font are available (#593); the 5x7 bitmap font otherwise — see the font note below the table |
| `gfx_text_width` | `gfx_text_width of [text, scale?]` or `of "text"` | Pixel width of `text` under the active text renderer: TTF metrics when active, `len * 6 * scale` in bitmap mode. Works before `gfx_open` |
| `gfx_text_height` | `gfx_text_height of scale?` | Pixel line height under the active text renderer: TTF font height when active, `7 * scale` in bitmap mode |
Expand Down
6 changes: 6 additions & 0 deletions lib/ui_theme.eigs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ _theme_dark is {
"tooltip_bg": [50, 50, 65],
"tooltip_text": [220, 220, 230],
"disabled_overlay": [25, 25, 35],
"hover_shade": [255, 255, 255, 22],
"pressed_shade": [0, 0, 0, 70],
"selection_bg": [80, 140, 220],
"font_scale": 2,
"char_w": 12,
Expand Down Expand Up @@ -100,6 +102,8 @@ _theme_light is {
"tooltip_bg": [50, 50, 60],
"tooltip_text": [240, 240, 245],
"disabled_overlay": [235, 235, 240],
"hover_shade": [0, 0, 0, 14],
"pressed_shade": [0, 0, 0, 36],
"selection_bg": [40, 110, 200],
"font_scale": 2,
"char_w": 12,
Expand Down Expand Up @@ -150,6 +154,8 @@ _theme_high_contrast is {
"tooltip_bg": [40, 40, 0],
"tooltip_text": [255, 255, 255],
"disabled_overlay": [0, 0, 0],
"hover_shade": [255, 255, 255, 50],
"pressed_shade": [255, 255, 255, 80],
"selection_bg": [255, 255, 0],
"font_scale": 2,
"char_w": 12,
Expand Down
27 changes: 25 additions & 2 deletions lib/ui_w_button.eigs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
# UI Widgets — Buttons: button, toggle_button, checkbox, toggle
# ============================================================

# Optional per-widget color overrides (#566): set `bg` / `text_color`
# on the returned dict ([r, g, b]) for semantic coloring (record-red,
# play-green, ...). Absent fields fall back to the theme, matching the
# toggle_button (color_on/color_off) and label (color) pattern.
define button(id, x, y, w, h, text, on_click) as:
return {
"type": "button",
Expand Down Expand Up @@ -60,16 +64,35 @@ define toggle(id, x, y, value, on_change) as:
# ---- Render functions ----

define _render_button(widget, ax, ay) as:
# Per-widget color overrides with theme fallback (#566)
bg is _theme.btn_bg
tc is _theme.btn_text
if widget.bg != null:
bg is widget.bg
if widget.text_color != null:
tc is widget.text_color
custom is 1
if widget.bg == null:
custom is 0
if widget.enabled == 0:
bg is _theme.btn_disabled
tc is _theme.btn_text_disabled
elif widget.pressed == 1:
elif custom == 0 and widget.pressed == 1:
bg is _theme.btn_pressed
elif widget.hover == 1:
elif custom == 0 and widget.hover == 1:
bg is _theme.btn_hover
_draw_rbox of [ax, ay, widget.w, widget.h, _theme.radius_sm, bg, _theme.panel_border]
# Hover/pressed feedback for custom-colored buttons: an alpha shade
# over the custom bg (a theme-color swap would erase the color).
# Theme dicts from set_theme may predate these keys — null skips.
if custom == 1 and widget.enabled == 1:
shade is null
if widget.pressed == 1:
shade is _theme.pressed_shade
elif widget.hover == 1:
shade is _theme.hover_shade
if shade != null:
gfx_rrect of [ax, ay, widget.w, widget.h, _theme.radius_sm, shade[0], shade[1], shade[2], shade[3]]
# Center text in button
tw is text_width of [widget.label, _theme.font_scale]
th is text_height of _theme.font_scale
Expand Down
12 changes: 6 additions & 6 deletions tests/run_all_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2060,15 +2060,15 @@ fi
echo ""

# [63] UI toolkit unit tests (headless, stubs gfx)
echo "[63] UI Toolkit (115 checks)"
echo "[63] UI Toolkit (118 checks)"
UI_OUTPUT=$(./eigenscript ../tests/test_ui.eigs 2>&1); UI_OUTPUT_RC=$?
if rc_ok "$UI_OUTPUT_RC" "$UI_OUTPUT" && echo "$UI_OUTPUT" | grep -q "All tests passed"; then
TOTAL=$((TOTAL + 115))
PASS=$((PASS + 115))
echo " PASS: all 115 UI toolkit checks"
TOTAL=$((TOTAL + 118))
PASS=$((PASS + 118))
echo " PASS: all 118 UI toolkit checks"
else
TOTAL=$((TOTAL + 115))
FAIL=$((FAIL + 115))
TOTAL=$((TOTAL + 118))
FAIL=$((FAIL + 118))
echo " FAIL: UI toolkit tests"
echo "$UI_OUTPUT" | grep -iE "assert|error|FAIL" | head -5
fi
Expand Down
20 changes: 20 additions & 0 deletions tests/test_ui.eigs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ t is toggle of ["tog1", 0, 0, 0, null]
assert_eq of [t.type, "toggle", "toggle type"]
assert_eq of [t.value, 0, "toggle default off"]

# ---- Per-widget button colors (#566) ----
# Render every state with custom bg/text_color set: the render path must
# not error (gfx stubs are no-ops) and must not clobber the overrides.
bc is button of ["btnc", 0, 0, 80, 24, "Rec", null]
bc.bg is [200, 40, 40]
bc.text_color is [255, 255, 255]
_render_button of [bc, 0, 0]
bc.hover is 1
_render_button of [bc, 0, 0]
bc.pressed is 1
_render_button of [bc, 0, 0]
bc.enabled is 0
_render_button of [bc, 0, 0]
assert_eq of [bc.bg[0], 200, "custom button bg preserved across renders"]
assert_eq of [bc.text_color[0], 255, "custom button text color preserved"]
bd is button of ["btnd", 0, 0, 80, 24, "Plain", null]
bd.hover is 1
_render_button of [bd, 0, 0]
assert_eq of [bd.bg, null, "plain button has no bg override"]

# ============================================================
# 2. Registry tests
# ============================================================
Expand Down
Loading