Files
espresso_frame/server/app/widgets/__init__.py
T
tfaour 37bd657299
Build and push server image / test (push) Successful in 49s
Build and push server image / build-and-push (push) Successful in 1m56s
Widget system Phase 2: full cutover to widget-based rendering
device.py's mode-keyed dispatch is replaced by a real compositor:
load a frame's widgets, compute pixel rects via app/grid.py, render
each through its widget module, and composite with render_panel.
Physical NEXT/BACK buttons now execute each frame's assigned
FrameButtonAction rows instead of one hardcoded per-mode action.

api_frames.py, manage.py, and common.py's build_manage_content are
repointed to read/write the frame's widget config rows instead of
the old Frame columns, and every settings page (Photos/Calendar/
Whiteboard tabs) now pre-fills its form from the same widget config
the write endpoints actually save to -- previously the read and
write sides would have silently diverged. The old mode selector and
photo-inlay checkbox are removed along with their now-inert wiring;
arbitrary widget placement subsumes what the fixed inlay split did.

Ships together with Phase 1 (per-type render/action modules) since
splitting the read/write cutover across deploys would have left
settings changes with no visible effect.
2026-07-24 09:26:28 -04:00

46 lines
2.0 KiB
Python

"""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 . import calendar, photos, whiteboard
WIDGET_TYPES = {
"photos": photos,
"calendar": calendar,
"whiteboard": whiteboard,
}