"""Registry mapping a Widget's widget_type to its render/action module -- the widget-system's analogue of routers/device.py's old RENDERERS/ ADVANCE_RENDERERS/BACK_RENDERERS dicts, generalized from "one mode owns the whole panel" to "each widget renders into its own region and optionally responds to named button actions." Each module in this package exposes: render(db, frame, widget, target_w, target_h, is_normal_wake=True) -> Image.Image An RGB image exactly target_w x target_h, unquantized -- the widget's content composed into its own region. Never returns packed panel bytes or raises for a foreseeable failure (a widget's own fetch hiccup shows a small placeholder instead) -- image_pipeline.render_panel composites every widget's own render() result onto one shared canvas and quantizes/packs the whole thing once (see its own docstring). is_normal_wake distinguishes an ordinary /frame/image GET from a button- triggered re-render -- only app/widgets/calendar.py's render() actually uses it (resetting browse_offset back to "today" on a normal wake), but every module accepts it for one uniform call signature regardless. ACTIONS: dict[str, Callable[[Session, Frame, Widget], None]] Named button actions this widget type supports (e.g. "advance", "back", "check_now") -- see models.FrameButtonAction. Each function mutates the widget's own state via db.widget_locked internally; none return a value or re-render themselves -- whatever dispatches a button press (routers/device.py, once the widget-system cutover lands) re-renders the whole panel once after running every assigned action. ACTION_LABELS: dict[str, str] Human-readable labels for ACTIONS' keys, for the button- assignment UI. """ from __future__ import annotations from ..models import FrameButtonAction from . import battery, calendar, photos, static_image, tasks, text, weather, whiteboard WIDGET_TYPES = { "photos": photos, "calendar": calendar, "whiteboard": whiteboard, "tasks": tasks, "static": static_image, "text": text, "weather": weather, "battery": battery, } def default_button_actions(frame_id: int, widget_id: int, widget_type: str) -> list[FrameButtonAction]: """NEXT/BACK -> whatever this widget's own advance/back concept is. Called both when backfilling pre-widget-system frames (see app/migration.py) and when a widget is newly created (see routers/api_widgets.py's api_widget_create) -- a widget is never left without a sane starting binding, so the physical buttons always do something reasonable for it until someone deliberately reassigns them in that widget's own config dialog.""" if widget_type == "whiteboard": # No real "next"/"back" concept for a static board -- both # buttons mean "check now". return [ FrameButtonAction(frame_id=frame_id, button="next", widget_id=widget_id, action="check_now"), FrameButtonAction(frame_id=frame_id, button="back", widget_id=widget_id, action="check_now"), ] if widget_type == "weather": return [ FrameButtonAction(frame_id=frame_id, button="next", widget_id=widget_id, action="check_now"), FrameButtonAction(frame_id=frame_id, button="back", widget_id=widget_id, action="check_now"), ] if widget_type in ("photos", "calendar"): return [ FrameButtonAction(frame_id=frame_id, button="next", widget_id=widget_id, action="advance"), FrameButtonAction(frame_id=frame_id, button="back", widget_id=widget_id, action="back"), ] return []