theme config, layout preview, container alignment

Server: ThemeConfig entity + CRUD (GET/PUT /theme), SQLite persistence,
ThemeUpdate broadcast to ESP32 on save and initial connect.
Client: render engine uses theme colors, full-screen redraw on theme change.
SPA: theme page with color pickers + presets, layout preview with TS port
of layout engine, justify/align controls on containers.
DisplayHint refactored to struct (kind + h_align + v_align).
This commit is contained in:
2026-06-19 03:26:18 +02:00
parent 81a4167382
commit fe59b68c37
46 changed files with 1276 additions and 118 deletions

View File

@@ -1,5 +1,7 @@
use client_domain::{BoundingBox, LayoutEngine, RenderTree};
use domain::{AlignItems, ContainerNode, Direction, JustifyContent, LayoutChild, LayoutNode, Sizing};
use domain::{
AlignItems, ContainerNode, Direction, JustifyContent, LayoutChild, LayoutNode, Sizing,
};
fn screen() -> BoundingBox {
BoundingBox::screen(240, 320)
@@ -230,8 +232,14 @@ fn justify_center_centers_fixed_children_on_main_axis() {
});
let tree = LayoutEngine::compute(&layout, screen());
assert_eq!(tree.get_widget_bounds(1), Some(&BoundingBox::new(80, 0, 40, 320)));
assert_eq!(tree.get_widget_bounds(2), Some(&BoundingBox::new(120, 0, 40, 320)));
assert_eq!(
tree.get_widget_bounds(1),
Some(&BoundingBox::new(80, 0, 40, 320))
);
assert_eq!(
tree.get_widget_bounds(2),
Some(&BoundingBox::new(120, 0, 40, 320))
);
}
#[test]
@@ -247,7 +255,10 @@ fn justify_end_pushes_to_end() {
});
let tree = LayoutEngine::compute(&layout, screen());
assert_eq!(tree.get_widget_bounds(1), Some(&BoundingBox::new(200, 0, 40, 320)));
assert_eq!(
tree.get_widget_bounds(1),
Some(&BoundingBox::new(200, 0, 40, 320))
);
}
#[test]
@@ -263,9 +274,18 @@ fn justify_space_between_distributes_gaps() {
});
let tree = LayoutEngine::compute(&layout, screen());
assert_eq!(tree.get_widget_bounds(1), Some(&BoundingBox::new(0, 0, 40, 320)));
assert_eq!(tree.get_widget_bounds(2), Some(&BoundingBox::new(100, 0, 40, 320)));
assert_eq!(tree.get_widget_bounds(3), Some(&BoundingBox::new(200, 0, 40, 320)));
assert_eq!(
tree.get_widget_bounds(1),
Some(&BoundingBox::new(0, 0, 40, 320))
);
assert_eq!(
tree.get_widget_bounds(2),
Some(&BoundingBox::new(100, 0, 40, 320))
);
assert_eq!(
tree.get_widget_bounds(3),
Some(&BoundingBox::new(200, 0, 40, 320))
);
}
#[test]
@@ -282,8 +302,14 @@ fn justify_space_evenly_distributes_with_edges() {
let tree = LayoutEngine::compute(&layout, screen());
// 160 / 3 = 53px per slot
assert_eq!(tree.get_widget_bounds(1), Some(&BoundingBox::new(53, 0, 40, 320)));
assert_eq!(tree.get_widget_bounds(2), Some(&BoundingBox::new(146, 0, 40, 320)));
assert_eq!(
tree.get_widget_bounds(1),
Some(&BoundingBox::new(53, 0, 40, 320))
);
assert_eq!(
tree.get_widget_bounds(2),
Some(&BoundingBox::new(146, 0, 40, 320))
);
}
// --- AlignItems tests ---
@@ -315,5 +341,8 @@ fn align_items_center_centers_on_cross_axis() {
let tree = LayoutEngine::compute(&layout, screen());
// Stretch: child gets full cross-axis height
assert_eq!(tree.get_widget_bounds(1), Some(&BoundingBox::new(0, 0, 40, 320)));
assert_eq!(
tree.get_widget_bounds(1),
Some(&BoundingBox::new(0, 0, 40, 320))
);
}