rsnaker/graphics/graphic_block.rs
1/// This module contains definitions and implementations for graphical blocks used in the game logic.
2///
3/// It includes the following components:
4///
5/// - [`GraphicBlock`]: A struct representing a graphical block that can be placed in the game logic world.
6/// - [`Position`]: A struct representing the position of a graphical block in a 2D space.
7use ratatui::buffer::Buffer;
8use ratatui::layout::Rect;
9use ratatui::prelude::{Span, Style};
10use ratatui::widgets::{Widget, WidgetRef};
11
12/// A struct representing a graphical block that can be displayed in the game logic.
13/// It holds the position and visual representation (image) of the block.
14///
15/// # Fields
16/// - `position`: The position of the graphic block on the screen.
17/// - `image`: The visual representation of the block, using a styled `Span`.
18#[derive(Clone, Debug, PartialEq)]
19pub struct GraphicBlock<'a> {
20 pub(crate) position: Position,
21 image: Span<'a>,
22 pub enabled: bool,
23}
24
25/// A struct representing the position of a graphical block in the 2D space.
26/// It holds the x and y coordinates of the block.
27///
28/// # Fields
29/// - `x`: The x-coordinate of the block's position.
30/// - `y`: The y-coordinate of the block's position.
31#[derive(Clone, Debug, PartialEq)]
32pub struct Position {
33 pub x: u16,
34 pub y: u16,
35}
36
37impl<'a> GraphicBlock<'a> {
38 /// Creates a new `GraphicBlock` with the specified position, image, and style.
39 ///
40 /// # Parameters
41 /// - `position`: The position of the graphic block.
42 /// - `image`: The string that represents the image of the block.
43 /// - `style`: The style applied to the image of the block.
44 ///
45 /// # Returns
46 /// A new `GraphicBlock` instance with the given position, image, and style.
47 ///
48 /// # Note
49 /// If you want less lifetime propagation, this code can be refactorised to take to use a static string for the image using `'static` lifetime.
50 /// But for a more general use, the lifetime parameter is better, allowing dynamic text (e.g., changing each time)
51 /// If you want no lifetime and dynamic text, add an own `String`, to `GraphicBlock` and generate for each rendering a new `Span`,
52 /// but this might affect performance.
53 #[must_use]
54 pub fn new(position: Position, image: &'a str, style: Style) -> GraphicBlock<'a> {
55 GraphicBlock {
56 position,
57 image: Span::styled(image, style),
58 enabled: true,
59 }
60 }
61
62 /// Sets the position of the graphic block.
63 ///
64 /// # Parameters
65 /// - `position`: The new position to set for the block.
66 pub fn set_position(&mut self, position: Position) {
67 self.position = position;
68 }
69
70 pub fn enable(&mut self) {
71 self.enabled = true;
72 }
73 pub fn disable(&mut self) {
74 self.enabled = false;
75 }
76 /// Retrieves the current position of the graphic block.
77 ///
78 /// # Returns
79 /// A reference to the current position of the graphic block.
80 #[must_use]
81 pub fn get_position(&self) -> &Position {
82 &self.position
83 }
84 /// Retrieves the current position of the graphic block.
85 ///
86 /// # Returns
87 /// A reference to the current position of the graphic block.
88 #[must_use]
89 pub fn get_content(&self) -> &str {
90 &self.image.content
91 }
92}
93///NB: Could also have used impl Widget for &T even if more limited
94/// see: <https://github.com/ratatui/ratatui/discussions/1274>
95impl WidgetRef for GraphicBlock<'_> {
96 /// Renders the graphic block into the given buffer.
97 ///
98 /// # Parameters
99 /// - `area`: The area in the terminal where the graphic block should be rendered.
100 /// - `buf`: A mutable reference to the buffer where the block will be drawn.
101 ///
102 /// # Behavior
103 /// If the block is outside the visible area, it will not be drawn, avoiding any panics.
104 /// The game logic will not crash even if part of the block is outside the visible area due to window resizing.
105 fn render_ref(&self, area: Rect, buf: &mut Buffer) {
106 if self.enabled
107 && area.y + self.position.y < area.height
108 && area.x + self.position.x < area.width
109 {
110 buf.set_span(
111 area.x + self.position.x,
112 area.y + self.position.y,
113 &self.image,
114 area.width,
115 );
116 }
117 }
118}
119
120/// Backward compatibility implementation for the `Widget` trait.
121impl Widget for GraphicBlock<'_> {
122 /// Renders the graphic block into the given buffer, using the `render_ref` method.
123 ///
124 /// # Parameters
125 /// - `area`: The area in the terminal where the graphic block should be rendered.
126 /// - `buf`: A mutable reference to the buffer where the block will be drawn.
127 fn render(self, area: Rect, buf: &mut Buffer) {
128 self.render_ref(area, buf);
129 }
130}