lumen_core/
layout_backend.rs1use crate::app::App;
10use crate::backends::{BackendEntry, Backends, missing, registered};
11use crate::traits::LayoutEngine;
12use bevy_ecs::schedule::SystemSet;
13
14#[derive(Clone, Copy, Debug)]
16pub struct LayoutBackend {
17 pub name: &'static str,
19 pub priority: i32,
22 pub engine: fn() -> Box<dyn LayoutEngine>,
25}
26
27impl BackendEntry for LayoutBackend {
28 const KIND: &'static str = "layout";
29
30 fn name(&self) -> &'static str {
31 self.name
32 }
33
34 fn priority(&self) -> i32 {
35 self.priority
36 }
37}
38
39pub type LayoutBackends = Backends<LayoutBackend>;
43
44#[derive(SystemSet, Clone, Copy, Debug, Hash, PartialEq, Eq)]
50pub struct LayoutSolve;
51
52pub fn install_layout(app: &mut App) -> Result<&'static str, String> {
55 let backend = registered::<LayoutBackend>(app)
56 .preferred()
57 .ok_or_else(missing::<LayoutBackend>)?;
58 (backend.engine)().install(app);
59 Ok(backend.name)
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::backends::register_backend;
66 use crate::components::Transform;
67 use crate::tick::TickStage;
68 use bevy_ecs::prelude::*;
69
70 struct Nudge;
72
73 fn nudge(mut boxes: Query<&mut Transform>) {
74 for mut t in &mut boxes {
75 t.absolute.x += 1.0;
76 }
77 }
78
79 impl LayoutEngine for Nudge {
80 fn install(self: Box<Self>, app: &mut App) {
81 app.add_systems(TickStage::LayoutSync, nudge.in_set(LayoutSolve));
82 }
83 }
84
85 fn nudge_engine() -> Box<dyn LayoutEngine> {
86 Box::new(Nudge)
87 }
88
89 #[derive(Resource, Default)]
91 struct Seen(Vec<f32>);
92
93 fn after_solve(boxes: Query<&Transform>, mut seen: ResMut<Seen>) {
94 seen.0.extend(boxes.iter().map(|t| t.absolute.x));
95 }
96
97 #[test]
99 fn a_build_without_an_engine_says_so() {
100 let mut app = App::new();
101 assert_eq!(
102 install_layout(&mut app).expect_err("none registered"),
103 "this build carries no layout backend"
104 );
105 }
106
107 #[test]
111 fn the_preferred_engine_installs_and_its_solve_runs_first() {
112 let mut app = App::new();
113 register_backend(
114 &mut app,
115 LayoutBackend {
116 name: "nudge",
117 priority: 1,
118 engine: nudge_engine,
119 },
120 );
121 app.world.init_resource::<Seen>();
122 app.add_systems(TickStage::LayoutSync, after_solve.after(LayoutSolve));
123 assert_eq!(install_layout(&mut app), Ok("nudge"));
124 app.world.spawn(Transform::default());
125
126 app.tick();
127 app.tick();
128 assert_eq!(app.world.resource::<Seen>().0, [1.0, 2.0]);
129 }
130}