diff --git a/openpilot/starpilot/ui/feature_settings.py b/openpilot/starpilot/ui/feature_settings.py index 3078460aee..baa304c58e 100644 --- a/openpilot/starpilot/ui/feature_settings.py +++ b/openpilot/starpilot/ui/feature_settings.py @@ -114,8 +114,11 @@ class FeatureSettingsView: draw_rounded_stroke(button, CONTROL_BORDER) if not row.repair_value: self.fonts.draw("-", FontRole.MEDIUM, 39, 1815, y + 25, TEXT_PRIMARY) - text = self._elide(f"Set {row.repair_value}", FontRole.MEDIUM, 29, 125) if row.repair_value else "+" - self.fonts.draw(text, FontRole.MEDIUM, 29 if row.repair_value else 39, 1960, y + 25, TEXT_PRIMARY) + text = f"Set {row.repair_value}" if row.repair_value else "+" + size = 29 if row.repair_value else 39 + if row.repair_value: + size *= min(1, 125 / self.fonts.measure(text, FontRole.MEDIUM, size).width) + self.fonts.draw(text, FontRole.MEDIUM, size, 1960, y + 25, TEXT_PRIMARY) finally: clip.end_scissor_mode() self.fonts.draw("Previous", FontRole.MEDIUM, 30, 1000, 993, diff --git a/openpilot/starpilot/ui/settings_geometry.py b/openpilot/starpilot/ui/settings_geometry.py index 9c66b3f0e9..3f71ebdb9e 100644 --- a/openpilot/starpilot/ui/settings_geometry.py +++ b/openpilot/starpilot/ui/settings_geometry.py @@ -85,6 +85,8 @@ def draw_hud_background(rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0, else: off = i * 2.5 * glow a = int(25 * (1.0 - i / 5) * glow) + if a <= 0: + continue gr = rl.Rectangle(rx - off, ry - off, rw + off * 2, rh + off * 2) draw_rounded_fill(gr, rl.Color(accent.r, accent.g, accent.b, max(0, min(255, a))), radius_px=radius_px) diff --git a/openpilot/starpilot/ui/tests/test_conditional_feature.py b/openpilot/starpilot/ui/tests/test_conditional_feature.py index 94017e80b0..aa79d80001 100644 --- a/openpilot/starpilot/ui/tests/test_conditional_feature.py +++ b/openpilot/starpilot/ui/tests/test_conditional_feature.py @@ -22,7 +22,7 @@ from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner from openpilot.starpilot.ui.feature_settings_state import FeatureInput, FeaturePage, FeatureRow, FeatureSettingsRequest, row_change from openpilot.starpilot.ui import feature_settings_compact as compact from openpilot.starpilot.ui import feature_settings as large -from openpilot.starpilot.ui.presentation import BitmapFonts, Profile +from openpilot.starpilot.ui.presentation import Profile def required_change(row: FeatureRow, direction: int = 1) -> FeatureSettingsRequest: @@ -521,12 +521,15 @@ class ConditionalFeatureTests(unittest.TestCase): def test_large_pane_renders_mode_and_child_labels_in_existing_geometry(self): labels = [] - fonts = BitmapFonts.__new__(BitmapFonts) - fonts.profile = Profile.LARGE + fonts = SimpleNamespace(profile=Profile.LARGE, + draw=lambda value, *_args: labels.append(value), + measure=lambda value, _role, size: SimpleNamespace(width=len(value) * size * .5), + vertical_ink=lambda _value, _role, size: (size * .2, size * .8)) state = self.owner.snapshot(FeaturePage.CONDITIONAL, parked=True, system_long=False, lateral_context=False, metric=False) - with patch.object(fonts, "draw", side_effect=lambda value, *_args, **_kwargs: labels.append(value)), \ - patch.object(large.rl, "draw_rectangle_rounded"), patch.object(large.clip, "begin_scissor_mode"), \ + with patch.object(large.rl, "draw_rectangle_rounded"), patch.object(large.rl, "draw_rectangle_rounded_lines_ex"), \ + patch.object(large.rl, "draw_line"), patch.object(large.rl, "draw_line_ex"), patch.object(large.rl, "draw_circle"), \ + patch.object(large.clip, "begin_scissor_mode"), \ patch.object(large.clip, "end_scissor_mode"): large.FeatureSettingsView(fonts).render(state) self.assertIn("Conditional Driving Modes", labels) diff --git a/openpilot/starpilot/ui/tests/test_feature_settings_lateral.py b/openpilot/starpilot/ui/tests/test_feature_settings_lateral.py index 80fb095802..f78df040a1 100644 --- a/openpilot/starpilot/ui/tests/test_feature_settings_lateral.py +++ b/openpilot/starpilot/ui/tests/test_feature_settings_lateral.py @@ -26,7 +26,7 @@ from openpilot.starpilot.ui.feature_settings_owner import FeatureSettingsOwner from openpilot.starpilot.ui.feature_settings_state import FeatureInput, FeatureRow, FeatureSettingsRequest, FeatureSettingsState, row_change from openpilot.starpilot.ui import feature_settings_compact as compact from openpilot.starpilot.ui import feature_settings as large -from openpilot.starpilot.ui.presentation import BitmapFonts, Profile +from openpilot.starpilot.ui.presentation import Profile def required_change(row: FeatureRow, direction: int = 1) -> FeatureSettingsRequest: @@ -609,25 +609,22 @@ class LateralFeatureSettingsTests(unittest.TestCase): self.assertTrue(any(item.text == "lkas press set off" for item in pushed[-1].items)) def test_large_repair_labels_match_requested_values(self): - class Fonts(BitmapFonts): - profile = Profile.LARGE - - def __init__(self): - self.text = [] - - def draw(self, value, *args, **kwargs): - self.text.append(value) - - fonts = Fonts() + text = [] + fonts = SimpleNamespace(profile=Profile.LARGE, + draw=lambda value, *_args: text.append(value), + measure=lambda value, _role, size: SimpleNamespace(width=len(value) * size * .5), + vertical_ink=lambda _value, _role, size: (size * .2, size * .8)) state = FeatureSettingsState(rows=( FeatureRow("AolBrakePauseSpeedMps", "Brake pause below", "Invalid", available=True, repair_value="0"), FeatureRow("LKASButtonControl", "LKAS press", "Unsupported", available=True, repair_value="Off"), )) - with patch.object(large.rl, "draw_rectangle_rounded"), patch.object(large.clip, "begin_scissor_mode"), \ + with patch.object(large.rl, "draw_rectangle_rounded"), patch.object(large.rl, "draw_rectangle_rounded_lines_ex"), \ + patch.object(large.rl, "draw_line"), patch.object(large.rl, "draw_line_ex"), patch.object(large.rl, "draw_circle"), \ + patch.object(large.clip, "begin_scissor_mode"), \ patch.object(large.clip, "end_scissor_mode"): large.FeatureSettingsView(fonts).render(state) - self.assertIn("Set 0", fonts.text) - self.assertIn("Set Off", fonts.text) + self.assertIn("Set 0", text) + self.assertIn("Set Off", text) def test_starpilot_tuning_help_is_visible_in_large_native_settings(self): class Fonts: diff --git a/openpilot/starpilot/ui/tests/test_feature_settings_visuals.py b/openpilot/starpilot/ui/tests/test_feature_settings_visuals.py index deb441aa80..894ba263a4 100644 --- a/openpilot/starpilot/ui/tests/test_feature_settings_visuals.py +++ b/openpilot/starpilot/ui/tests/test_feature_settings_visuals.py @@ -15,7 +15,7 @@ from openpilot.starpilot.ui.presentation import FontRole, Profile def fake_fonts(): return SimpleNamespace(profile=Profile.LARGE, draw=Mock(), - measure=lambda text, *_args: SimpleNamespace(width=len(text) * 10), + measure=lambda text, _role, size: SimpleNamespace(width=len(text) * size * .5), vertical_ink=lambda *_args: (6, 33)) @@ -53,6 +53,7 @@ class FeatureVisualTests(unittest.TestCase): stars.reset_mock() geometry.draw_aether_toggle(rect, value, available=available, seed_id="lane:switch") self.assertEqual(stars.call_count, int(available and value)) + self.assertTrue(all(call.args[-1].a > 0 for call in rl.draw_rectangle_rounded.call_args_list)) knob = next(call.args[0] for call in rl.draw_rectangle_rounded.call_args_list if call.args[0].width == 44) centers[available, value] = knob.x + knob.width / 2 @@ -87,6 +88,23 @@ class FeatureVisualTests(unittest.TestCase): self.assertIn(text, texts) healthy = next(call for call in fonts.draw.call_args_list if call.args[0] == "Healthy") self.assertEqual(healthy.args[-1], geometry.TEXT_SECONDARY) + plus = next(call for call in fonts.draw.call_args_list if call.args[0] == "+") + self.assertEqual(plus.args[2], 39) + + def test_repair_targets_stay_complete_and_fit_the_button(self): + self.drawing() + fonts = fake_fonts() + renderer = view.FeatureSettingsView(fonts) + for target in ("0", "Off", "On", "Stock", "Reset", "Auto", "30 s", "10 s", "4.0", "starpilot"): + with self.subTest(target=target): + fonts.draw.reset_mock() + row = FeatureRow("repair", "Repair", "Invalid", available=True, repair_value=target) + renderer.render(FeatureSettingsState(rows=(row,))) + label = next(call for call in fonts.draw.call_args_list if call.args[0] == f"Set {target}") + self.assertLessEqual(fonts.measure(*label.args[:3]).width, 125) + self.assertTrue(0 < label.args[2] <= 29) + if target == "Off": + self.assertEqual(label.args[2], 29) def test_long_text_fits_and_subtitle_ink_clears_rows(self): self.drawing() @@ -98,9 +116,9 @@ class FeatureVisualTests(unittest.TestCase): renderer.render(state) decoration.assert_not_called() title = next(call for call in fonts.draw.call_args_list if call.args[1] == FontRole.SEMI_BOLD) - self.assertLessEqual(fonts.measure(title.args[0]).width, 1305) + self.assertLessEqual(fonts.measure(*title.args[:3]).width, 1305) label = next(call for call in fonts.draw.call_args_list if call.args[0].startswith("Long label")) - self.assertLessEqual(fonts.measure(label.args[0]).width, 1149) + self.assertLessEqual(fonts.measure(*label.args[:3]).width, 1149) subtitle = next(call for call in fonts.draw.call_args_list if call.args[0] == state.subtitle) self.assertLess(subtitle.args[4] + 33, 130) self.assertEqual(renderer._elide("Fits", FontRole.NORMAL, 27, 100), "Fits")