add edit step + do recipe in main view, added carousel for recipe selection
This commit is contained in:
88
frontend/views/list_select/base.py
Normal file
88
frontend/views/list_select/base.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List
|
||||
|
||||
from PIL import ImageDraw
|
||||
|
||||
from .item import ListItem
|
||||
|
||||
|
||||
class SelectableList(ABC):
|
||||
"""Abstract base for list and carousel selection widgets.
|
||||
|
||||
Manages a collection of :class:`ListItem` objects with a single
|
||||
*selected_index*, wrapping navigation, and a shared highlight-bar
|
||||
drawing primitive. Concrete subclasses (:class:`ListView`,
|
||||
:class:`CarouselView`) only differ in how they render the items.
|
||||
|
||||
The two subclasses expose an identical public interface so they can
|
||||
be swapped interchangeably.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
items: List[ListItem] | None = None,
|
||||
x_offset: int = 40,
|
||||
max_visible: int = 5,
|
||||
item_height: int = 20):
|
||||
self._items: List[ListItem] = list(items or [])
|
||||
self.selected_index: int = 0
|
||||
self.x_offset = x_offset
|
||||
self.max_visible = max_visible
|
||||
self.item_height = item_height
|
||||
|
||||
# ---- item access ------------------------------------------------
|
||||
|
||||
@property
|
||||
def items(self) -> List[ListItem]:
|
||||
return self._items
|
||||
|
||||
@items.setter
|
||||
def items(self, value: List[ListItem]):
|
||||
self._items = list(value)
|
||||
if self._items and self.selected_index >= len(self._items):
|
||||
self.selected_index = len(self._items) - 1
|
||||
elif not self._items:
|
||||
self.selected_index = 0
|
||||
|
||||
@property
|
||||
def selected_item(self) -> ListItem | None:
|
||||
if self._items and 0 <= self.selected_index < len(self._items):
|
||||
return self._items[self.selected_index]
|
||||
return None
|
||||
|
||||
# ---- navigation -------------------------------------------------
|
||||
|
||||
def select_previous(self):
|
||||
"""Move selection to the previous item (wraps around)."""
|
||||
if self._items:
|
||||
self.selected_index = (self.selected_index - 1) % len(self._items)
|
||||
|
||||
def select_next(self):
|
||||
"""Move selection to the next item (wraps around)."""
|
||||
if self._items:
|
||||
self.selected_index = (self.selected_index + 1) % len(self._items)
|
||||
|
||||
# ---- drawing helpers --------------------------------------------
|
||||
|
||||
def _draw_highlight(self,
|
||||
draw: ImageDraw.ImageDraw,
|
||||
x: int,
|
||||
y_center: int,
|
||||
font_size: int = 10,
|
||||
width: int = 90):
|
||||
"""Draw a rounded highlight bar via overlapping circles.
|
||||
|
||||
The bar radius is derived from *font_size* so the bar always
|
||||
fits snugly around the rendered text.
|
||||
"""
|
||||
r = font_size // 2 + 4
|
||||
for i in range(0, width, max(1, r // 2)):
|
||||
draw.circle((x + i, y_center), r, fill='black')
|
||||
|
||||
# ---- rendering (subclass) ---------------------------------------
|
||||
|
||||
@abstractmethod
|
||||
def render(self, draw: ImageDraw.ImageDraw, y_start: int) -> None:
|
||||
"""Render the widget onto *draw* starting at *y_start*."""
|
||||
...
|
||||
Reference in New Issue
Block a user