Add a per-widget text-size picker for calendar/tasks legibility
Calendar and tasks pack the most body text at the smallest default sizes, so those two gear-icon dialogs get a "Text size" card (Normal/ Large/X-Large) alongside the existing Border card -- a new Widget-level font_scale column with its own POST .../font-scale endpoint, same Widget-property-not-config-field shape as border_style. Threaded through every classic (calendar_render.py) and modern (html_render.py/ calendar_html_render.py) size calc via one shared panel_style. scaled_size() so row heights/max_rows already derived from font size re-fit around the bigger text automatically.
This commit is contained in:
@@ -26,7 +26,7 @@ from pydantic import BaseModel
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .. import calendar_render, grid, photo_queue, quiet_hours, weather, weather_render, webdav_client
|
||||
from .. import calendar_render, grid, panel_style, photo_queue, quiet_hours, weather, weather_render, webdav_client
|
||||
from ..auth import require_frame_control, require_frame_view, require_user_api
|
||||
from ..db import frame_locked, get_db, widget_locked
|
||||
from ..image_pipeline import (
|
||||
@@ -96,7 +96,7 @@ def _widget_dict(w: Widget, locked: bool = False) -> dict:
|
||||
return {"id": w.id, "widget_type": w.widget_type, "x": w.x, "y": w.y, "w": w.w, "h": w.h,
|
||||
"sort_order": w.sort_order, "border_style": w.border_style,
|
||||
"border_thickness": w.border_thickness, "border_color_index": w.border_color_index,
|
||||
"locked": locked}
|
||||
"font_scale": w.font_scale, "locked": locked}
|
||||
|
||||
|
||||
def require_widget_view(
|
||||
@@ -280,6 +280,31 @@ def api_widget_border(
|
||||
return _widget_dict(widget)
|
||||
|
||||
|
||||
class WidgetFontScaleRequest(BaseModel):
|
||||
font_scale: float
|
||||
|
||||
|
||||
@router.post("/api/frames/{frame_id}/widgets/{widget_id}/font-scale")
|
||||
def api_widget_font_scale(
|
||||
body: WidgetFontScaleRequest, frame_widget: tuple[Frame, Widget] = Depends(require_widget_control),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Sets this widget's text-size multiplier (the Layout dialog's "Text
|
||||
size" picker) -- a shared Widget-level property (see models.Widget.
|
||||
font_scale), not a per-type config field, since any widget type with
|
||||
body text can use it. Its own endpoint for the same reason
|
||||
api_widget_border has one: api_widget_config_save's per-type dispatch
|
||||
edits a config row via widget_locked, and font_scale lives on Widget
|
||||
itself, not any per-type config table."""
|
||||
frame, widget = frame_widget
|
||||
if body.font_scale not in panel_style.FONT_SCALE_CHOICES:
|
||||
raise HTTPException(400, f"font_scale must be one of {panel_style.FONT_SCALE_CHOICES}")
|
||||
with frame_locked(db, frame.id):
|
||||
widget.font_scale = body.font_scale
|
||||
db.commit()
|
||||
return _widget_dict(widget)
|
||||
|
||||
|
||||
@router.delete("/api/frames/{frame_id}/widgets/{widget_id}")
|
||||
def api_widget_delete(
|
||||
widget_id: int, frame: Frame = Depends(require_frame_control), db: Session = Depends(get_db)
|
||||
@@ -876,7 +901,7 @@ def api_widget_preview_calendar(
|
||||
img = calendar_html_render.build(
|
||||
events, ccfg.view, ccfg.browse_offset, target_w, target_h, tz, ccfg.week_start, frame.palette_rgb,
|
||||
weather_cities, ccfg.weather_units, ccfg.week_days, ccfg.week_layout, ccfg.week_start_offset,
|
||||
frame.theme,
|
||||
frame.theme, widget.font_scale,
|
||||
)
|
||||
quantized = _quantize(img, frame.palette_rgb, dither_strength=1.0)
|
||||
png = _png_bytes(quantized)
|
||||
@@ -887,7 +912,7 @@ def api_widget_preview_calendar(
|
||||
week_start=ccfg.week_start,
|
||||
weather_cities=weather_cities, weather_units=ccfg.weather_units,
|
||||
week_days=ccfg.week_days, week_layout=ccfg.week_layout,
|
||||
week_start_offset=ccfg.week_start_offset,
|
||||
week_start_offset=ccfg.week_start_offset, font_scale=widget.font_scale,
|
||||
)
|
||||
return Response(content=png, media_type="image/png")
|
||||
|
||||
@@ -912,12 +937,14 @@ def api_widget_preview_tasks(
|
||||
from .. import html_render
|
||||
|
||||
target_w, target_h = logical_render_size(frame.orientation)
|
||||
img = html_render.build_tasks(tasks, target_w, target_h, frame.palette_rgb, title, frame.theme)
|
||||
img = html_render.build_tasks(tasks, target_w, target_h, frame.palette_rgb, title, frame.theme,
|
||||
widget.font_scale)
|
||||
quantized = _quantize(img, frame.palette_rgb, dither_strength=1.0)
|
||||
png = _png_bytes(quantized)
|
||||
else:
|
||||
png = calendar_render.render_tasks_preview_png(tasks, orientation=frame.orientation,
|
||||
palette_rgb=frame.palette_rgb, title=title)
|
||||
palette_rgb=frame.palette_rgb, title=title,
|
||||
font_scale=widget.font_scale)
|
||||
return Response(content=png, media_type="image/png")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user