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}