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