1use crate::runner::UI_ROOT_NAME;
4use nightshade::ecs::ui::state::UiStateTrait;
5use nightshade::ecs::ui::units::UiValue;
6use nightshade::prelude::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ScreenAnchor {
11 TopLeft,
12 TopRight,
13 BottomLeft,
14 BottomRight,
15 Center,
16}
17
18pub fn spawn_text(world: &mut World, text: &str, anchor: ScreenAnchor) -> Entity {
22 let root = ui_root(world);
23 let (position, anchor_kind, alignment) = anchor_window(anchor);
24 let entity = {
25 let mut tree = UiTreeBuilder::from_parent(world, root);
26 tree.add_node()
27 .window(position, Ab(vec2(960.0, 96.0)), anchor_kind)
28 .with_text(text, 18.0)
29 .with_text_alignment(alignment, VerticalAlignment::Middle)
30 .with_text_outline(Vec4::new(0.0, 0.0, 0.0, 1.0), 0.15)
31 .color_raw::<UiBase>(Vec4::new(1.0, 1.0, 1.0, 1.0))
32 .without_pointer_events()
33 .entity()
34 };
35 ui_mark_render_dirty(world);
36 entity
37}
38
39pub fn spawn_label(world: &mut World, text: &str, position: Vec3) -> Entity {
41 spawn_3d_billboard_text_with_properties(
42 world,
43 text,
44 position,
45 TextProperties {
46 font_size: 24.0,
47 alignment: TextAlignment::Center,
48 vertical_alignment: VerticalAlignment::Middle,
49 ..Default::default()
50 },
51 )
52}
53
54pub fn set_text_color(world: &mut World, entity: Entity, color: [f32; 4]) {
57 let rgba = Vec4::new(color[0], color[1], color[2], color[3]);
58 if is_screen_text(world, entity) {
59 if let Some(node_color) = world.ui.get_ui_node_color_mut(entity) {
60 node_color.colors[UiBase::INDEX] = Some(rgba);
61 }
62 ui_mark_render_dirty(world);
63 return;
64 }
65 if let Some(component) = world.core.get_text_mut(entity) {
66 component.set_color(rgba);
67 }
68}
69
70pub fn set_text_size(world: &mut World, entity: Entity, size: f32) {
73 if is_screen_text(world, entity) {
74 if let Some(UiNodeContent::Text {
75 font_size_override, ..
76 }) = world.ui.get_ui_node_content_mut(entity)
77 {
78 *font_size_override = Some(size);
79 }
80 ui_mark_render_dirty(world);
81 return;
82 }
83 if let Some(component) = world.core.get_text_mut(entity) {
84 component.set_font_size(size);
85 }
86}
87
88pub fn set_text(world: &mut World, entity: Entity, text: &str) {
91 if let Some(slot) = screen_text_slot(world, entity) {
92 if world.resources.text.cache.get_text(slot) == Some(text) {
93 return;
94 }
95 world.resources.text.cache.set_text(slot, text);
96 ui_mark_render_dirty(world);
97 return;
98 }
99 let Some(text_index) = world
100 .core
101 .get_text(entity)
102 .map(|component| component.text_index)
103 else {
104 return;
105 };
106 if world.resources.text.cache.get_text(text_index) == Some(text) {
107 return;
108 }
109 world.resources.text.cache.set_text(text_index, text);
110 if let Some(component) = world.core.get_text_mut(entity) {
111 component.dirty = true;
112 }
113}
114
115fn ui_root(world: &mut World) -> Entity {
116 if let Some(&root) = world.resources.entities.names.get(UI_ROOT_NAME)
117 && world.ui.get_ui_layout_root(root).is_some()
118 {
119 return root;
120 }
121 let root = UiTreeBuilder::new(world).finish();
122 world
123 .resources
124 .entities
125 .names
126 .insert(UI_ROOT_NAME.to_string(), root);
127 root
128}
129
130fn is_screen_text(world: &World, entity: Entity) -> bool {
131 matches!(
132 world.ui.get_ui_node_content(entity),
133 Some(UiNodeContent::Text { .. })
134 )
135}
136
137fn screen_text_slot(world: &World, entity: Entity) -> Option<usize> {
138 match world.ui.get_ui_node_content(entity) {
139 Some(UiNodeContent::Text { text_slot, .. }) => Some(*text_slot),
140 _ => None,
141 }
142}
143
144fn anchor_window(anchor: ScreenAnchor) -> (UiValue<Vec2>, Anchor, TextAlignment) {
145 match anchor {
146 ScreenAnchor::TopLeft => (
147 Ab(vec2(20.0, 16.0)).into(),
148 Anchor::TopLeft,
149 TextAlignment::Left,
150 ),
151 ScreenAnchor::TopRight => (
152 Rl(vec2(100.0, 0.0)) + Ab(vec2(-20.0, 16.0)),
153 Anchor::TopRight,
154 TextAlignment::Right,
155 ),
156 ScreenAnchor::BottomLeft => (
157 Rl(vec2(0.0, 100.0)) + Ab(vec2(20.0, -16.0)),
158 Anchor::BottomLeft,
159 TextAlignment::Left,
160 ),
161 ScreenAnchor::BottomRight => (
162 Rl(vec2(100.0, 100.0)) + Ab(vec2(-20.0, -16.0)),
163 Anchor::BottomRight,
164 TextAlignment::Right,
165 ),
166 ScreenAnchor::Center => (
167 Rl(vec2(50.0, 50.0)).into(),
168 Anchor::Center,
169 TextAlignment::Center,
170 ),
171 }
172}