1use std::{collections::HashMap, rc::Rc};
2
3use crate::{
4 Alignment, GameCommand, GameCommandsArg, IntCoords2d, Layer, Query, QueryResultList, Rgb,
5 System, SystemsGenerator, TerminalCamera, TerminalRenderer, TerminalTextCharacter,
6 TerminalTransform, Text, UiAnchor, WorldText, EVENT_UPDATE,
7};
8
9pub struct TerminalUiRendererSystemsGenerator {}
12impl TerminalUiRendererSystemsGenerator {
13 pub fn new() -> Self {
14 Self {}
15 }
16}
17impl SystemsGenerator for TerminalUiRendererSystemsGenerator {
18 fn generate(&self) -> Vec<(&'static str, System)> {
19 vec![(
20 EVENT_UPDATE,
21 System::new(
22 vec![
23 Query::new().has::<Text>(),
24 Query::new().has::<WorldText>().has::<TerminalTransform>(),
25 Query::new()
26 .has::<TerminalTextCharacter>()
27 .has::<TerminalRenderer>(),
28 Query::new()
29 .has_where::<TerminalCamera>(|cam| cam.is_main)
30 .has::<TerminalTransform>(),
31 ],
32 update_text_ui,
33 ),
34 )]
35 }
36}
37
38fn update_text_ui(results: Vec<QueryResultList>, commands: GameCommandsArg) {
39 if let [text_results, world_text_results, drawn_text_results, main_cam_results, ..] =
40 &results[..]
41 {
42 let main_cam = main_cam_results.get_only::<TerminalCamera>();
43 let main_cam_transform = main_cam_results.get_only::<TerminalTransform>();
44
45 let anchor_positions = get_anchor_positions(&main_cam, &main_cam_transform);
46
47 wipe_existing_text(drawn_text_results, Rc::clone(&commands));
48
49 for text_result in text_results {
50 let text = text_result.components().get::<Text>();
51
52 let (anchor_x, anchor_y) = anchor_positions
53 .get(&text.anchor)
54 .expect("The anchor position can be determined.")
55 .values();
56
57 let chars = text.value.chars().collect::<Vec<char>>();
58
59 let justification_offset = get_justification_offset(&text.justification, chars.len());
60
61 let starting_position =
62 IntCoords2d::new(anchor_x, anchor_y) + justification_offset + text.offset;
63
64 add_text_entities(
65 &chars,
66 &starting_position,
67 &text.foreground_color,
68 &text.background_color,
69 Rc::clone(&commands),
70 );
71 }
72
73 for world_text_result in world_text_results {
74 let world_text = world_text_result.components().get::<WorldText>();
75 let world_text_transform = world_text_result.components().get::<TerminalTransform>();
76
77 let chars = world_text.value.chars().collect::<Vec<char>>();
78
79 let justification_offset =
80 get_justification_offset(&world_text.justification, chars.len());
81
82 let starting_position =
83 world_text_transform.coords + justification_offset + world_text.offset;
84
85 add_text_entities(
86 &chars,
87 &starting_position,
88 &world_text.foreground_color,
89 &world_text.background_color,
90 Rc::clone(&commands),
91 );
92 }
93 }
94}
95
96fn wipe_existing_text(text_character_query_results: &QueryResultList, commands: GameCommandsArg) {
97 for text_character in text_character_query_results {
98 commands
99 .borrow_mut()
100 .issue(GameCommand::DestroyEntity(*text_character.entity()));
101 }
102}
103
104fn get_anchor_positions(
105 main_camera: &TerminalCamera,
106 main_camera_transform: &TerminalTransform,
107) -> HashMap<UiAnchor, IntCoords2d> {
108 let (zero_indexed_width, zero_indexed_height) = (
109 main_camera.field_of_view.width() as i64 - 1,
110 main_camera.field_of_view.height() as i64 - 1,
111 );
112
113 let base_coords = main_camera_transform.coords;
114
115 HashMap::from([
116 (UiAnchor::TopLeft, base_coords),
117 (
118 UiAnchor::MiddleTop,
119 base_coords + IntCoords2d::new(zero_indexed_width / 2, 0),
120 ),
121 (
122 UiAnchor::TopRight,
123 base_coords + IntCoords2d::new(zero_indexed_width, 0),
124 ),
125 (
126 UiAnchor::MiddleRight,
127 base_coords + IntCoords2d::new(zero_indexed_width, zero_indexed_height / 2),
128 ),
129 (
130 UiAnchor::BottomRight,
131 base_coords + IntCoords2d::new(zero_indexed_width, zero_indexed_height),
132 ),
133 (
134 UiAnchor::MiddleBottom,
135 base_coords + IntCoords2d::new(zero_indexed_width / 2, zero_indexed_height),
136 ),
137 (
138 UiAnchor::BottomLeft,
139 base_coords + IntCoords2d::new(0, zero_indexed_height),
140 ),
141 (
142 UiAnchor::MiddleLeft,
143 base_coords + IntCoords2d::new(0, zero_indexed_height / 2),
144 ),
145 (
146 UiAnchor::Middle,
147 base_coords + IntCoords2d::new(zero_indexed_width / 2, zero_indexed_height / 2),
148 ),
149 ])
150}
151
152fn get_justification_offset(justification: &Alignment, text_length: usize) -> IntCoords2d {
153 match justification {
154 Alignment::Left => IntCoords2d::zero(),
155 Alignment::Middle => IntCoords2d::new(-((text_length / 2) as i64), 0),
156 Alignment::Right => IntCoords2d::new(-(text_length as i64), 0),
157 }
158}
159
160fn add_text_entities(
161 chars: &Vec<char>,
162 starting_position: &IntCoords2d,
163 foreground_color: &Option<Rgb>,
164 background_color: &Option<Rgb>,
165 commands: GameCommandsArg,
166) {
167 let mut offset = IntCoords2d::zero();
168 for character in chars {
169 commands.borrow_mut().issue(GameCommand::AddEntity(vec![
170 Box::new(TerminalTextCharacter {}),
171 Box::new(TerminalRenderer {
172 display: *character,
173 layer: Layer::below(&Layer::furthest_foreground()),
174 foreground_color: *foreground_color,
175 background_color: *background_color,
176 }),
177 Box::new(TerminalTransform {
178 coords: *starting_position + offset,
179 }),
180 ]));
181
182 offset += IntCoords2d::right();
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 mod test_update_text_ui {
191 use std::cell::RefCell;
192
193 use crate::{Dimensions2d, Entity, QueryResult, StoredComponentList};
194
195 use super::*;
196
197 mod text {
198 use super::*;
199
200 mod with_camera_at_origin {
201 use crate::{Component, GameCommandQueue};
202
203 use super::*;
204
205 fn make_basic_results() -> Vec<QueryResultList> {
206 let results = vec![
207 QueryResultList::new(vec![]),
208 QueryResultList::new(vec![]),
209 QueryResultList::new(vec![]),
210 QueryResultList::new(vec![QueryResult::new(
211 Entity(1),
212 StoredComponentList::new(vec![
213 Rc::new(RefCell::new(Box::new(TerminalCamera {
214 field_of_view: Dimensions2d::new(10, 10),
215 is_main: true,
216 }))),
217 Rc::new(RefCell::new(Box::new(TerminalTransform {
218 coords: IntCoords2d::zero(),
219 }))),
220 ]),
221 )]),
222 ];
223
224 results
225 }
226
227 #[test]
228 fn pos_is_correct_for_top_left() {
229 let mut results = make_basic_results();
230
231 results[0].push(QueryResult::new(
232 Entity(0),
233 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
234 value: String::from("T"),
235 anchor: UiAnchor::TopLeft,
236 justification: Alignment::Left,
237 offset: IntCoords2d::zero(),
238 foreground_color: None,
239 background_color: None,
240 })))]),
241 ));
242
243 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
244
245 update_text_ui(results, Rc::clone(&commands));
246
247 assert!(commands
248 .borrow()
249 .queue()
250 .iter()
251 .find(|c| match c {
252 GameCommand::AddEntity(comps) => {
253 comps
254 .iter()
255 .find(|comp| {
256 comp.component_name() == TerminalTransform::name()
257 && (TerminalTransform::cast(&***comp)).unwrap().coords
258 == IntCoords2d::new(0, 0)
259 })
260 .is_some()
261 }
262 _ => {
263 false
264 }
265 })
266 .is_some());
267 }
268
269 #[test]
270 fn pos_is_correct_for_middle_top() {
271 let mut results = make_basic_results();
272
273 results[0].push(QueryResult::new(
274 Entity(0),
275 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
276 value: String::from("T"),
277 anchor: UiAnchor::MiddleTop,
278 justification: Alignment::Left,
279 offset: IntCoords2d::zero(),
280 foreground_color: None,
281 background_color: None,
282 })))]),
283 ));
284
285 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
286
287 update_text_ui(results, Rc::clone(&commands));
288
289 assert!(commands
290 .borrow()
291 .queue()
292 .iter()
293 .find(|c| match c {
294 GameCommand::AddEntity(comps) => {
295 comps
296 .iter()
297 .find(|comp| {
298 comp.component_name() == TerminalTransform::name()
299 && (TerminalTransform::cast(&***comp)).unwrap().coords
300 == IntCoords2d::new(4, 0)
301 })
302 .is_some()
303 }
304 _ => {
305 false
306 }
307 })
308 .is_some());
309 }
310
311 #[test]
312 fn pos_is_correct_for_top_right() {
313 let mut results = make_basic_results();
314
315 results[0].push(QueryResult::new(
316 Entity(0),
317 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
318 value: String::from("T"),
319 anchor: UiAnchor::TopRight,
320 justification: Alignment::Left,
321 offset: IntCoords2d::zero(),
322 foreground_color: None,
323 background_color: None,
324 })))]),
325 ));
326
327 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
328
329 update_text_ui(results, Rc::clone(&commands));
330
331 assert!(commands
332 .borrow()
333 .queue()
334 .iter()
335 .find(|c| match c {
336 GameCommand::AddEntity(comps) => {
337 comps
338 .iter()
339 .find(|comp| {
340 comp.component_name() == TerminalTransform::name()
341 && (TerminalTransform::cast(&***comp)).unwrap().coords
342 == IntCoords2d::new(9, 0)
343 })
344 .is_some()
345 }
346 _ => {
347 false
348 }
349 })
350 .is_some());
351 }
352
353 #[test]
354 fn pos_is_correct_for_middle_right() {
355 let mut results = make_basic_results();
356
357 results[0].push(QueryResult::new(
358 Entity(0),
359 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
360 value: String::from("T"),
361 anchor: UiAnchor::MiddleRight,
362 justification: Alignment::Left,
363 offset: IntCoords2d::zero(),
364 foreground_color: None,
365 background_color: None,
366 })))]),
367 ));
368
369 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
370
371 update_text_ui(results, Rc::clone(&commands));
372
373 assert!(commands
374 .borrow()
375 .queue()
376 .iter()
377 .find(|c| match c {
378 GameCommand::AddEntity(comps) => {
379 comps
380 .iter()
381 .find(|comp| {
382 comp.component_name() == TerminalTransform::name()
383 && (TerminalTransform::cast(&***comp)).unwrap().coords
384 == IntCoords2d::new(9, 4)
385 })
386 .is_some()
387 }
388 _ => {
389 false
390 }
391 })
392 .is_some());
393 }
394
395 #[test]
396 fn pos_is_correct_for_bottom_right() {
397 let mut results = make_basic_results();
398
399 results[0].push(QueryResult::new(
400 Entity(0),
401 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
402 value: String::from("T"),
403 anchor: UiAnchor::BottomRight,
404 justification: Alignment::Left,
405 offset: IntCoords2d::zero(),
406 foreground_color: None,
407 background_color: None,
408 })))]),
409 ));
410
411 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
412
413 update_text_ui(results, Rc::clone(&commands));
414
415 assert!(commands
416 .borrow()
417 .queue()
418 .iter()
419 .find(|c| match c {
420 GameCommand::AddEntity(comps) => {
421 comps
422 .iter()
423 .find(|comp| {
424 comp.component_name() == TerminalTransform::name()
425 && (TerminalTransform::cast(&***comp)).unwrap().coords
426 == IntCoords2d::new(9, 9)
427 })
428 .is_some()
429 }
430 _ => {
431 false
432 }
433 })
434 .is_some());
435 }
436
437 #[test]
438 fn pos_is_correct_for_middle_bottom() {
439 let mut results = make_basic_results();
440
441 results[0].push(QueryResult::new(
442 Entity(0),
443 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
444 value: String::from("T"),
445 anchor: UiAnchor::MiddleBottom,
446 justification: Alignment::Left,
447 offset: IntCoords2d::zero(),
448 foreground_color: None,
449 background_color: None,
450 })))]),
451 ));
452
453 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
454
455 update_text_ui(results, Rc::clone(&commands));
456
457 assert!(commands
458 .borrow()
459 .queue()
460 .iter()
461 .find(|c| match c {
462 GameCommand::AddEntity(comps) => {
463 comps
464 .iter()
465 .find(|comp| {
466 comp.component_name() == TerminalTransform::name()
467 && (TerminalTransform::cast(&***comp)).unwrap().coords
468 == IntCoords2d::new(4, 9)
469 })
470 .is_some()
471 }
472 _ => {
473 false
474 }
475 })
476 .is_some());
477 }
478
479 #[test]
480 fn pos_is_correct_for_bottom_left() {
481 let mut results = make_basic_results();
482
483 results[0].push(QueryResult::new(
484 Entity(0),
485 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
486 value: String::from("T"),
487 anchor: UiAnchor::BottomLeft,
488 justification: Alignment::Left,
489 offset: IntCoords2d::zero(),
490 foreground_color: None,
491 background_color: None,
492 })))]),
493 ));
494
495 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
496
497 update_text_ui(results, Rc::clone(&commands));
498
499 assert!(commands
500 .borrow()
501 .queue()
502 .iter()
503 .find(|c| match c {
504 GameCommand::AddEntity(comps) => {
505 comps
506 .iter()
507 .find(|comp| {
508 comp.component_name() == TerminalTransform::name()
509 && (TerminalTransform::cast(&***comp)).unwrap().coords
510 == IntCoords2d::new(0, 9)
511 })
512 .is_some()
513 }
514 _ => {
515 false
516 }
517 })
518 .is_some());
519 }
520
521 #[test]
522 fn pos_is_correct_for_middle_left() {
523 let mut results = make_basic_results();
524
525 results[0].push(QueryResult::new(
526 Entity(0),
527 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
528 value: String::from("T"),
529 anchor: UiAnchor::MiddleLeft,
530 justification: Alignment::Left,
531 offset: IntCoords2d::zero(),
532 foreground_color: None,
533 background_color: None,
534 })))]),
535 ));
536
537 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
538
539 update_text_ui(results, Rc::clone(&commands));
540
541 assert!(commands
542 .borrow()
543 .queue()
544 .iter()
545 .find(|c| match c {
546 GameCommand::AddEntity(comps) => {
547 comps
548 .iter()
549 .find(|comp| {
550 comp.component_name() == TerminalTransform::name()
551 && (TerminalTransform::cast(&***comp)).unwrap().coords
552 == IntCoords2d::new(0, 4)
553 })
554 .is_some()
555 }
556 _ => {
557 false
558 }
559 })
560 .is_some());
561 }
562 }
563
564 mod with_camera_offset_from_origin {
565 use super::*;
566 use crate::{Component, GameCommandQueue};
567
568 fn make_basic_results() -> Vec<QueryResultList> {
569 let results = vec![
570 QueryResultList::new(vec![]),
571 QueryResultList::new(vec![]),
572 QueryResultList::new(vec![]),
573 QueryResultList::new(vec![QueryResult::new(
574 Entity(1),
575 StoredComponentList::new(vec![
576 Rc::new(RefCell::new(Box::new(TerminalCamera {
577 field_of_view: Dimensions2d::new(5, 5),
578 is_main: true,
579 }))),
580 Rc::new(RefCell::new(Box::new(TerminalTransform {
581 coords: IntCoords2d::new(-3, 2),
582 }))),
583 ]),
584 )]),
585 ];
586
587 results
588 }
589
590 #[test]
591 fn pos_is_correct_for_top_left() {
592 let mut results = make_basic_results();
593
594 results[0].push(QueryResult::new(
595 Entity(0),
596 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
597 value: String::from("T"),
598 anchor: UiAnchor::TopLeft,
599 justification: Alignment::Left,
600 offset: IntCoords2d::zero(),
601 foreground_color: None,
602 background_color: None,
603 })))]),
604 ));
605
606 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
607
608 update_text_ui(results, Rc::clone(&commands));
609
610 assert!(commands
611 .borrow()
612 .queue()
613 .iter()
614 .find(|c| match c {
615 GameCommand::AddEntity(comps) => {
616 comps
617 .iter()
618 .find(|comp| {
619 comp.component_name() == TerminalTransform::name()
620 && (TerminalTransform::cast(&***comp)).unwrap().coords
621 == IntCoords2d::new(-3, 2)
622 })
623 .is_some()
624 }
625 _ => {
626 false
627 }
628 })
629 .is_some());
630 }
631
632 #[test]
633 fn pos_is_correct_for_middle_top() {
634 let mut results = make_basic_results();
635
636 results[0].push(QueryResult::new(
637 Entity(0),
638 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
639 value: String::from("T"),
640 anchor: UiAnchor::MiddleTop,
641 justification: Alignment::Left,
642 offset: IntCoords2d::zero(),
643 foreground_color: None,
644 background_color: None,
645 })))]),
646 ));
647
648 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
649
650 update_text_ui(results, Rc::clone(&commands));
651
652 assert!(commands
653 .borrow()
654 .queue()
655 .iter()
656 .find(|c| match c {
657 GameCommand::AddEntity(comps) => {
658 comps
659 .iter()
660 .find(|comp| {
661 comp.component_name() == TerminalTransform::name()
662 && (TerminalTransform::cast(&***comp)).unwrap().coords
663 == IntCoords2d::new(-1, 2)
664 })
665 .is_some()
666 }
667 _ => {
668 false
669 }
670 })
671 .is_some());
672 }
673
674 #[test]
675 fn pos_is_correct_for_top_right() {
676 let mut results = make_basic_results();
677
678 results[0].push(QueryResult::new(
679 Entity(0),
680 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
681 value: String::from("T"),
682 anchor: UiAnchor::TopRight,
683 justification: Alignment::Left,
684 offset: IntCoords2d::zero(),
685 foreground_color: None,
686 background_color: None,
687 })))]),
688 ));
689
690 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
691
692 update_text_ui(results, Rc::clone(&commands));
693
694 assert!(commands
695 .borrow()
696 .queue()
697 .iter()
698 .find(|c| match c {
699 GameCommand::AddEntity(comps) => {
700 comps
701 .iter()
702 .find(|comp| {
703 comp.component_name() == TerminalTransform::name()
704 && (TerminalTransform::cast(&***comp)).unwrap().coords
705 == IntCoords2d::new(1, 2)
706 })
707 .is_some()
708 }
709 _ => {
710 false
711 }
712 })
713 .is_some());
714 }
715
716 #[test]
717 fn pos_is_correct_for_middle_right() {
718 let mut results = make_basic_results();
719
720 results[0].push(QueryResult::new(
721 Entity(0),
722 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
723 value: String::from("T"),
724 anchor: UiAnchor::MiddleRight,
725 justification: Alignment::Left,
726 offset: IntCoords2d::zero(),
727 foreground_color: None,
728 background_color: None,
729 })))]),
730 ));
731
732 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
733
734 update_text_ui(results, Rc::clone(&commands));
735
736 assert!(commands
737 .borrow()
738 .queue()
739 .iter()
740 .find(|c| match c {
741 GameCommand::AddEntity(comps) => {
742 comps
743 .iter()
744 .find(|comp| {
745 comp.component_name() == TerminalTransform::name()
746 && (TerminalTransform::cast(&***comp)).unwrap().coords
747 == IntCoords2d::new(1, 4)
748 })
749 .is_some()
750 }
751 _ => {
752 false
753 }
754 })
755 .is_some());
756 }
757
758 #[test]
759 fn pos_is_correct_for_bottom_right() {
760 let mut results = make_basic_results();
761
762 results[0].push(QueryResult::new(
763 Entity(0),
764 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
765 value: String::from("T"),
766 anchor: UiAnchor::BottomRight,
767 justification: Alignment::Left,
768 offset: IntCoords2d::zero(),
769 foreground_color: None,
770 background_color: None,
771 })))]),
772 ));
773
774 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
775
776 update_text_ui(results, Rc::clone(&commands));
777
778 assert!(commands
779 .borrow()
780 .queue()
781 .iter()
782 .find(|c| match c {
783 GameCommand::AddEntity(comps) => {
784 comps
785 .iter()
786 .find(|comp| {
787 comp.component_name() == TerminalTransform::name()
788 && (TerminalTransform::cast(&***comp)).unwrap().coords
789 == IntCoords2d::new(1, 6)
790 })
791 .is_some()
792 }
793 _ => {
794 false
795 }
796 })
797 .is_some());
798 }
799
800 #[test]
801 fn pos_is_correct_for_middle_bottom() {
802 let mut results = make_basic_results();
803
804 results[0].push(QueryResult::new(
805 Entity(0),
806 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
807 value: String::from("T"),
808 anchor: UiAnchor::MiddleBottom,
809 justification: Alignment::Left,
810 offset: IntCoords2d::zero(),
811 foreground_color: None,
812 background_color: None,
813 })))]),
814 ));
815
816 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
817
818 update_text_ui(results, Rc::clone(&commands));
819
820 assert!(commands
821 .borrow()
822 .queue()
823 .iter()
824 .find(|c| match c {
825 GameCommand::AddEntity(comps) => {
826 comps
827 .iter()
828 .find(|comp| {
829 comp.component_name() == TerminalTransform::name()
830 && (TerminalTransform::cast(&***comp)).unwrap().coords
831 == IntCoords2d::new(-1, 6)
832 })
833 .is_some()
834 }
835 _ => {
836 false
837 }
838 })
839 .is_some());
840 }
841
842 #[test]
843 fn pos_is_correct_for_bottom_left() {
844 let mut results = make_basic_results();
845
846 results[0].push(QueryResult::new(
847 Entity(0),
848 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
849 value: String::from("T"),
850 anchor: UiAnchor::BottomLeft,
851 justification: Alignment::Left,
852 offset: IntCoords2d::zero(),
853 foreground_color: None,
854 background_color: None,
855 })))]),
856 ));
857
858 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
859
860 update_text_ui(results, Rc::clone(&commands));
861
862 assert!(commands
863 .borrow()
864 .queue()
865 .iter()
866 .find(|c| match c {
867 GameCommand::AddEntity(comps) => {
868 comps
869 .iter()
870 .find(|comp| {
871 comp.component_name() == TerminalTransform::name()
872 && (TerminalTransform::cast(&***comp)).unwrap().coords
873 == IntCoords2d::new(-3, 6)
874 })
875 .is_some()
876 }
877 _ => {
878 false
879 }
880 })
881 .is_some());
882 }
883
884 #[test]
885 fn pos_is_correct_for_middle_left() {
886 let mut results = make_basic_results();
887
888 results[0].push(QueryResult::new(
889 Entity(0),
890 StoredComponentList::new(vec![Rc::new(RefCell::new(Box::new(Text {
891 value: String::from("T"),
892 anchor: UiAnchor::MiddleLeft,
893 justification: Alignment::Left,
894 offset: IntCoords2d::zero(),
895 foreground_color: None,
896 background_color: None,
897 })))]),
898 ));
899
900 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
901
902 update_text_ui(results, Rc::clone(&commands));
903
904 assert!(commands
905 .borrow()
906 .queue()
907 .iter()
908 .find(|c| match c {
909 GameCommand::AddEntity(comps) => {
910 comps
911 .iter()
912 .find(|comp| {
913 comp.component_name() == TerminalTransform::name()
914 && (TerminalTransform::cast(&***comp)).unwrap().coords
915 == IntCoords2d::new(-3, 4)
916 })
917 .is_some()
918 }
919 _ => {
920 false
921 }
922 })
923 .is_some());
924 }
925 }
926 }
927
928 mod world_text {
929 use crate::{Component, GameCommandQueue};
930
931 use super::*;
932
933 #[test]
934 fn text_goes_to_the_correct_position_with_origin_camera() {
935 let results = vec![
936 QueryResultList::new(vec![]),
937 QueryResultList::new(vec![QueryResult::new(
938 Entity(10),
939 StoredComponentList::new(vec![
940 Rc::new(RefCell::new(Box::new(WorldText {
941 value: String::from("T"),
942 justification: Alignment::Left,
943 offset: IntCoords2d::zero(),
944 background_color: None,
945 foreground_color: None,
946 }))),
947 Rc::new(RefCell::new(Box::new(TerminalTransform {
948 coords: IntCoords2d::new(5, 3),
949 }))),
950 ]),
951 )]),
952 QueryResultList::new(vec![]),
953 QueryResultList::new(vec![QueryResult::new(
954 Entity(1),
955 StoredComponentList::new(vec![
956 Rc::new(RefCell::new(Box::new(TerminalCamera {
957 field_of_view: Dimensions2d::new(10, 10),
958 is_main: true,
959 }))),
960 Rc::new(RefCell::new(Box::new(TerminalTransform {
961 coords: IntCoords2d::zero(),
962 }))),
963 ]),
964 )]),
965 ];
966
967 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
968
969 update_text_ui(results, Rc::clone(&commands));
970
971 assert!(commands
972 .borrow()
973 .queue()
974 .iter()
975 .find(|c| match c {
976 GameCommand::AddEntity(comps) => {
977 comps
978 .iter()
979 .find(|comp| {
980 comp.component_name() == TerminalTransform::name()
981 && (TerminalTransform::cast(&***comp)).unwrap().coords
982 == IntCoords2d::new(5, 3)
983 })
984 .is_some()
985 }
986 _ => {
987 false
988 }
989 })
990 .is_some());
991 }
992
993 #[test]
994 fn text_goes_to_the_correct_position_with_offset_camera() {
995 let results = vec![
996 QueryResultList::new(vec![]),
997 QueryResultList::new(vec![QueryResult::new(
998 Entity(10),
999 StoredComponentList::new(vec![
1000 Rc::new(RefCell::new(Box::new(WorldText {
1001 value: String::from("T"),
1002 justification: Alignment::Left,
1003 offset: IntCoords2d::zero(),
1004 background_color: None,
1005 foreground_color: None,
1006 }))),
1007 Rc::new(RefCell::new(Box::new(TerminalTransform {
1008 coords: IntCoords2d::new(5, 3),
1009 }))),
1010 ]),
1011 )]),
1012 QueryResultList::new(vec![]),
1013 QueryResultList::new(vec![QueryResult::new(
1014 Entity(1),
1015 StoredComponentList::new(vec![
1016 Rc::new(RefCell::new(Box::new(TerminalCamera {
1017 field_of_view: Dimensions2d::new(10, 10),
1018 is_main: true,
1019 }))),
1020 Rc::new(RefCell::new(Box::new(TerminalTransform {
1021 coords: IntCoords2d::new(-5, 8),
1022 }))),
1023 ]),
1024 )]),
1025 ];
1026
1027 let commands = Rc::new(RefCell::new(GameCommandQueue::new()));
1028
1029 update_text_ui(results, Rc::clone(&commands));
1030
1031 assert!(commands
1032 .borrow()
1033 .queue()
1034 .iter()
1035 .find(|c| match c {
1036 GameCommand::AddEntity(comps) => {
1037 comps
1038 .iter()
1039 .find(|comp| {
1040 comp.component_name() == TerminalTransform::name()
1041 && (TerminalTransform::cast(&***comp)).unwrap().coords
1042 == IntCoords2d::new(5, 3)
1043 })
1044 .is_some()
1045 }
1046 _ => {
1047 false
1048 }
1049 })
1050 .is_some());
1051 }
1052 }
1053 }
1054}