Frame.theme (7 presets in app/theme_tokens.py) drives font family, corner radius, drop shadow, and an accent hue for every modern-style widget's header/accent region. Rich accent colors (not just the 6 flat panel inks) are approximated via denser Bayer stippling confined to just that region (html_render.ordered_dither_regions), so icon/text content elsewhere stays exactly as crisp as it is today -- verified directly against real Chromium renders, both in unit tests and via run-server. "classic" is a byte-identical no-visual-change default: weather's header keeps its original fixed blue gradient, tasks/calendar keep their flat THEME_* ink. Themes are purely stylistic -- battery's charge-level color, calendar/ tasks' per-owner event chips, and text's own per-widget font choice are never touched.
85 lines
3.6 KiB
Python
85 lines
3.6 KiB
Python
"""Calendar widget: merged-event agenda/week/month view into the
|
|
widget's own region -- the widget-system analogue of routers/device.py's
|
|
old _render_calendar_mode/_advance_calendar_mode/_back_calendar_mode.
|
|
|
|
render() builds directly at whatever target box it's asked for --
|
|
calendar_render.py's layout math picks from discrete size tiers (see
|
|
its own _size_tier) rather than always laying out at full panel size and
|
|
resizing after the fact, so a small placed calendar widget gets an
|
|
actually-legible small-size layout instead of a shrunk-down full-size
|
|
one.
|
|
|
|
"Photo inlay" (calendar mode's old half-and-half photo split) has no
|
|
widget-system equivalent -- place an independent photo widget alongside
|
|
instead; arbitrary placement is strictly more flexible than one fixed
|
|
split ever was."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from PIL import Image
|
|
from sqlalchemy.orm import Session
|
|
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from ..calendar_render import _build
|
|
from ..db import widget_locked
|
|
from ..models import CalendarWidgetConfig, Frame, Widget
|
|
from ..routers.common import (
|
|
get_or_refresh_calendar_events_for_widget,
|
|
get_or_refresh_weather_for_widget,
|
|
)
|
|
|
|
ACTION_LABELS = {"advance": "Next period", "back": "Previous period"}
|
|
|
|
|
|
def render(db: Session, frame: Frame, widget: Widget, target_w: int, target_h: int,
|
|
is_normal_wake: bool = True) -> Image.Image:
|
|
"""is_normal_wake=True (an ordinary /frame/image GET, not a button
|
|
press) resets browse_offset back to "today" if it had drifted --
|
|
mirrors the old _render_calendar_mode's identical behavior. A button
|
|
press explicitly moved the browse position on purpose, so it passes
|
|
is_normal_wake=False to render its own already-updated offset instead
|
|
of immediately snapping back to 0."""
|
|
if is_normal_wake:
|
|
with widget_locked(db, frame.id, widget.id) as (_, _, locked_cfg):
|
|
if locked_cfg.browse_offset != 0:
|
|
locked_cfg.browse_offset = 0
|
|
|
|
cfg = db.get(CalendarWidgetConfig, widget.id)
|
|
events, fetch_summary = get_or_refresh_calendar_events_for_widget(db, frame, widget)
|
|
weather_cities = get_or_refresh_weather_for_widget(db, frame, widget) if cfg.weather_enabled else None
|
|
|
|
if cfg.render_style == "modern":
|
|
# Local import: html_render pulls in Playwright, a real headless-
|
|
# Chromium dependency -- every other widget type, and this one's
|
|
# own classic path, should never pay for it.
|
|
from .. import calendar_html_render
|
|
|
|
tz = ZoneInfo(frame.timezone) if frame.timezone else ZoneInfo("UTC")
|
|
return calendar_html_render.build(
|
|
events, cfg.view, cfg.browse_offset, target_w, target_h, tz, cfg.week_start, frame.palette_rgb,
|
|
weather_cities, cfg.weather_units, cfg.week_days, cfg.week_layout, cfg.week_start_offset,
|
|
frame.theme,
|
|
)
|
|
|
|
return _build(
|
|
events, view=cfg.view, browse_offset=cfg.browse_offset, target_w=target_w, target_h=target_h,
|
|
timezone=frame.timezone, fetch_summary=fetch_summary, week_start=cfg.week_start,
|
|
palette_rgb=frame.palette_rgb, weather_cities=weather_cities, weather_units=cfg.weather_units,
|
|
week_days=cfg.week_days, week_layout=cfg.week_layout,
|
|
week_start_offset=cfg.week_start_offset,
|
|
)
|
|
|
|
|
|
def _advance(db: Session, frame: Frame, widget: Widget) -> None:
|
|
with widget_locked(db, frame.id, widget.id) as (_, _, cfg):
|
|
cfg.browse_offset += 1
|
|
|
|
|
|
def _back(db: Session, frame: Frame, widget: Widget) -> None:
|
|
with widget_locked(db, frame.id, widget.id) as (_, _, cfg):
|
|
cfg.browse_offset -= 1
|
|
|
|
|
|
ACTIONS = {"advance": _advance, "back": _back}
|