Adds the actual "Android home screen" placement experience: a new Layout tab with a pointer-driven canvas for dragging/resizing widgets and adding new ones from a type picker. Backed by a new routers/api_widgets.py (create/move/delete), which re-validates bounds, minimum footprint, and no-overlap server-side regardless of what the client already checked. A widget added without an explicit position lands in the first open space that fits it (grid.find_open_rect), so users don't have to hunt for empty space themselves. Also fixes a real latent bug this surfaced: changing a frame's orientation swaps the widget grid's long/short axis, which left existing widget placements out of bounds on the new grid with no render-time safeguard. Orientation changes now reset the layout to a single full-panel widget (keeping the first widget's type, dropping the rest), with a client-side confirm before it happens.
47 lines
1.8 KiB
HTML
47 lines
1.8 KiB
HTML
{% extends "app_base.html" %}
|
|
|
|
{% block title %}{{ frame.name or "Frame" }} · Layout{% endblock %}
|
|
{% block page_title %}{% include "_frame_name_edit.html" %}{% endblock %}
|
|
|
|
{% block device_status %}{% include "_device_status_bar.html" %}{% endblock %}
|
|
{% block tabs %}{% include "_frame_tabs.html" %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="control-banner" class="control-banner" style="display: none;">
|
|
<span id="control-holder"></span>
|
|
<button type="button" id="take-control" class="btn-inline">Take control</button>
|
|
</div>
|
|
|
|
<div class="layout">
|
|
<div class="main-col">
|
|
<section class="card">
|
|
<h2 class="card-title">Widgets</h2>
|
|
<p class="sub">Drag a widget to move it, drag its bottom-right corner
|
|
to resize it -- like arranging widgets on a phone's home screen.
|
|
Widgets can't overlap. Each widget's own settings (which album,
|
|
which calendars, etc.) live on its type's own tab.</p>
|
|
<div id="widget-canvas-wrap">
|
|
<div id="widget-canvas"></div>
|
|
</div>
|
|
<p class="sub" id="widget-canvas-empty-hint" style="display: none; margin-top: 10px;">
|
|
Nothing placed yet -- add a widget below.</p>
|
|
</section>
|
|
|
|
<section class="card" style="margin-top: 20px;">
|
|
<h2 class="card-title">Add a widget</h2>
|
|
<div id="add-widget-buttons" class="checkbox-row" style="gap: 10px; flex-wrap: wrap;"></div>
|
|
<p class="sub" id="add-widget-hint" style="margin-top: 8px;"></p>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="result"></div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>window.FRAME_API = {{ ("/api/frames/" ~ frame.id) | tojson }};</script>
|
|
<script src="/static/device_status_bar.js"></script>
|
|
<script src="/static/frame_header.js"></script>
|
|
<script src="/static/frame_layout.js"></script>
|
|
{% endblock %}
|