1use std::sync::{Arc, RwLock};
27
28use khora_core::asset::font::Font;
29use khora_core::renderer::api::text::TextRenderer;
30use khora_core::renderer::GraphicsDevice;
31use khora_core::Runtime;
32
33use crate::assets::Assets;
34use crate::ecs::{SemanticDomain, World};
35use crate::flow::{Flow, Selection};
36use crate::register_flow;
37use crate::ui::components::{UiBorder, UiColor, UiImage, UiText, UiTransform};
38use crate::ui::{ExtractedUiNode, ExtractedUiText, UiScene};
39
40#[derive(Default)]
47pub struct UiFlow;
48
49impl Flow for UiFlow {
50 type View = UiScene;
51
52 const DOMAIN: SemanticDomain = SemanticDomain::Ui;
53 const NAME: &'static str = "ui";
54
55 fn project(&self, world: &World, _sel: &Selection, runtime: &Runtime) -> Self::View {
56 let surface_size = runtime
57 .backends
58 .get::<Arc<dyn GraphicsDevice>>()
59 .map(|d| d.get_surface_size())
60 .unwrap_or((0, 0));
61
62 let mut scene = UiScene {
63 surface_size,
64 ..Default::default()
65 };
66
67 extract_nodes(world, &mut scene);
68
69 if let (Some(text_renderer), Some(fonts_lock)) = (
70 runtime.backends.get::<Arc<dyn TextRenderer>>(),
71 runtime.resources.get::<Arc<RwLock<Assets<Font>>>>(),
72 ) {
73 if let Ok(fonts) = fonts_lock.read() {
74 layout_texts(world, text_renderer.as_ref(), &fonts, &mut scene);
75 }
76 }
77
78 scene.nodes.sort_by_key(|n| n.z_index);
79 scene.texts.sort_by_key(|t| t.z_index);
80 scene
81 }
82}
83
84register_flow!(UiFlow);
85
86fn extract_nodes(world: &World, scene: &mut UiScene) {
87 let query = world.query::<(
88 &UiTransform,
89 Option<&UiColor>,
90 Option<&UiBorder>,
91 Option<&UiImage>,
92 )>();
93 for (transform, color, border, image) in query {
94 scene.nodes.push(ExtractedUiNode {
95 pos: transform.pos,
96 size: transform.size,
97 color: color.copied(),
98 border: border.copied(),
99 image: image.copied(),
100 z_index: transform.z_index,
101 });
102 }
103}
104
105fn layout_texts(
106 world: &World,
107 text_renderer: &dyn TextRenderer,
108 fonts: &Assets<Font>,
109 scene: &mut UiScene,
110) {
111 for (transform, text) in world.query::<(&UiTransform, &UiText)>() {
112 if let Some(font_handle) = fonts.get(&text.font) {
113 let layout =
114 text_renderer.layout_text(&text.content, font_handle, text.font, text.size, None);
115 scene.texts.push(ExtractedUiText {
116 pos: transform.pos,
117 layout: std::sync::Arc::from(layout),
118 color: text.color,
119 z_index: transform.z_index,
120 });
121 }
122 }
123}