Files
frontend-dev/frontend/views/list_select/carousel.py

68 lines
2.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from PIL import ImageDraw
from .base import SelectableList
class CarouselView(SelectableList):
"""Single-item display shows the selected item centred and larger.
Position indicator dots at the bottom communicate how many items
exist and which one is currently selected. Uses
:pymethod:`ListItem.render_large` so callers can optionally provide
a more detailed rendering for carousel mode.
``large_font_size`` controls the font size passed to
:pymethod:`ListItem.render_large` (default 30, roughly 3× PIL default).
``center_x`` sets the horizontal centre used when drawing the item text
(default 84, the midpoint of a 168 px wide display). PIL's ``anchor='mm'``
is forwarded so the text is centred on that x coordinate.
``render_height`` is the total vertical space (in pixels) allocated to the
carousel; dots are placed at the very bottom of this region. Defaults to
``max_visible * item_height``.
"""
def __init__(self, *args,
large_font_size: int = 30,
center_x: int = 84,
render_height: int | None = None,
**kwargs):
super().__init__(*args, **kwargs)
self.large_font_size = large_font_size
self.center_x = center_x
self.render_height = render_height
def render(self, draw: ImageDraw.ImageDraw, y_start: int) -> None:
if not self._items:
return
item = self._items[self.selected_index]
height = self.render_height or (self.max_visible * self.item_height)
y_center = y_start + height // 2
# Render selected item centred (large variant, no highlight)
item.render_large(
draw,
(self.center_x, y_center),
fill='black',
font_size=self.large_font_size,
anchor='mm',
)
total = len(self._items)
# Position indicator dots at the top of the allocated area
if total > 1:
indicator_y = y_start
dot_spacing = min(8, max(4, 80 // max(total - 1, 1)))
total_width = (total - 1) * dot_spacing
start_x = self.center_x - total_width // 2
for i in range(total):
x = start_x + i * dot_spacing
r = 2 if i == self.selected_index else 1
draw.circle((x, indicator_y), r, fill='black')