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}