diff --git a/src/theme_nerv_fx.py b/src/theme_nerv_fx.py index e6a94c4..919bffe 100644 --- a/src/theme_nerv_fx.py +++ b/src/theme_nerv_fx.py @@ -18,3 +18,21 @@ class StatusFlicker: def get_alpha(self) -> float: # Modulate between 0.7 and 1.0 using sin wave return 0.85 + 0.15 * math.sin(time.time() * 20.0) + +class AlertPulsing: + def __init__(self): + self.active = False + + def update(self, status: str): + self.active = status.lower().startswith("error") + + def render(self, width: float, height: float): + if not self.active: + return + draw_list = imgui.get_foreground_draw_list() + # sin(t) is between -1 and 1 + # scale to 0 to 1: (sin(t) + 1) / 2 + # multiply by (0.2 - 0.05) = 0.15 and add 0.05 + alpha = 0.05 + 0.15 * ((math.sin(time.time() * 4.0) + 1.0) / 2.0) + color = imgui.get_color_u32((1.0, 0.0, 0.0, alpha)) + draw_list.add_rect((0.0, 0.0), (width, height), color, 0.0, 0, 10.0) diff --git a/tests/test_theme_nerv_alert.py b/tests/test_theme_nerv_alert.py new file mode 100644 index 0000000..b6c3eb5 --- /dev/null +++ b/tests/test_theme_nerv_alert.py @@ -0,0 +1,50 @@ +import pytest +from unittest.mock import MagicMock, patch +from imgui_bundle import imgui +from src.theme_nerv_fx import AlertPulsing + +def test_alert_pulsing_update(): + ap = AlertPulsing() + assert ap.active is False + + ap.update("error: something failed") + assert ap.active is True + + ap.update("ok: all good") + assert ap.active is False + + ap.update("Error: Case Insensitive") + assert ap.active is True + +def test_alert_pulsing_render_inactive(): + ap = AlertPulsing() + ap.active = False + + with patch("src.theme_nerv_fx.imgui.get_foreground_draw_list") as mock_get_draw_list: + ap.render(100.0, 100.0) + mock_get_draw_list.assert_not_called() + +def test_alert_pulsing_render_active(): + ap = AlertPulsing() + ap.active = True + + mock_draw_list = MagicMock() + with patch("src.theme_nerv_fx.imgui.get_foreground_draw_list", return_value=mock_draw_list) as mock_get_draw_list, \ + patch("src.theme_nerv_fx.imgui.get_color_u32", return_value=0xFF0000FF) as mock_get_color, \ + patch("time.time", return_value=1.0): + + ap.render(800.0, 600.0) + + mock_get_draw_list.assert_called_once() + mock_get_color.assert_called_once() + mock_draw_list.add_rect.assert_called_once() + + # Check arguments of add_rect + # add_rect(p_min, p_max, col, rounding, flags, thickness) + args, kwargs = mock_draw_list.add_rect.call_args + assert args[0] == (0.0, 0.0) + assert args[1] == (800.0, 600.0) + assert args[2] == 0xFF0000FF + assert args[3] == 0.0 + assert args[4] == 0 + assert args[5] == 10.0