ytesrev/drawable/
mod.rs

1//! Contains utilities for where and how to draw things
2
3extern crate sdl2;
4
5use sdl2::event::Event;
6use sdl2::rect::{Point, Rect};
7use sdl2::render::Canvas;
8use sdl2::video::Window;
9
10/// Where to draw a specific object.
11#[allow(unused)]
12#[derive(Clone, Copy, PartialEq, Debug)]
13pub enum Position {
14    /// Place the top left corner at a specific position, ignoring size. Only used in the most
15    /// primitive situations
16    TopLeftCorner(Point),
17    /// Center the object at this position
18    Center(Point),
19    /// Draw the object in a specific rectangle
20    Rect(Rect),
21}
22
23/// Settings to specify how an object is supposed to be drawed
24#[derive(Clone, Copy, PartialEq, Debug)]
25pub struct DrawSettings {
26    /// Is the object being drawed in the notes window, or the main window? In the notes window,
27    /// more debug information is drawed.
28    pub notes_view: bool,
29    /// Color of the background in RGB order
30    pub background_color: (u8, u8, u8),
31}
32
33/// The default draw settings for the main window
34pub const DSETTINGS_MAIN: DrawSettings = DrawSettings {
35    notes_view: false,
36    background_color: (255, 248, 234),
37};
38
39/// The default draw settings for the notes window
40pub const DSETTINGS_NOTES: DrawSettings = DrawSettings {
41    notes_view: true,
42    ..DSETTINGS_MAIN
43};
44
45/// The state for a specific object on screen, used in the [`Drawable::state()`]
46#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
47pub enum State {
48    /// The object is still in the process of being shown, such as fading in.
49    Working,
50    /// The object is complete and the next call to step will start to hide it
51    Final,
52    /// The object is being hidden
53    Hidden,
54}
55
56impl Position {
57    /// Convert a `Position` into a Rect with the specified size. The size is not gauranteed to be
58    /// (width, height) such as when the `Position` is a `Position::Rect` with a smaller size
59    ///
60    /// For `Position::Rect` the center is preserved, but the size is changed.
61    ///
62    /// ```
63    /// use ytesrev::{Point, Rect};
64    /// use ytesrev::drawable::Position;
65    ///
66    /// let tlc = Position::TopLeftCorner(Point::new(10, 20));
67    /// assert_eq!(tlc.into_rect_with_size(5, 8), Rect::new(10, 20, 5, 8));
68    ///
69    /// let center = Position::Center(Point::new(20, 30));
70    /// assert_eq!(center.into_rect_with_size(4, 10), Rect::new(18, 25, 4, 10));
71    ///
72    /// let rect = Position::Rect(Rect::new(10, 40, 20, 20));
73    /// assert_eq!(rect.into_rect_with_size(6, 4), Rect::new(17, 48, 6, 4));
74    ///
75    /// let rect_too_short = Position::Rect(Rect::new(10, 20, 30, 8));
76    /// assert_eq!(rect_too_short.into_rect_with_size(20, 69), Rect::new(15, 20, 20, 8));
77    ///
78    /// ```
79    pub fn into_rect_with_size(self, width: u32, height: u32) -> Rect {
80        match self {
81            Position::TopLeftCorner(point) => Rect::new(point.x, point.y, width, height),
82            Position::Center(point) => Rect::new(
83                point.x - width as i32 / 2,
84                point.y - height as i32 / 2,
85                width,
86                height,
87            ),
88            Position::Rect(rect) => {
89                let center_x = rect.x() + rect.width() as i32 / 2;
90                let center_y = rect.y() + rect.height() as i32 / 2;
91
92                let x = (center_x - width as i32 / 2).max(rect.x);
93                let y = (center_y - height as i32 / 2).max(rect.y);
94
95                let width = if x + width as i32 > rect.x + rect.width() as i32 {
96                    rect.width()
97                } else {
98                    width
99                };
100
101                let height = if y + height as i32 > rect.y + rect.height() as i32 {
102                    rect.height()
103                } else {
104                    height
105                };
106
107                Rect::new(x, y, width, height)
108            }
109        }
110    }
111
112    /// Convert a Position into a Rect, like [`Position::into_rect_with_size`], but this doesn't
113    /// check if the [`Position::Rect`] is too small.
114    /// ```
115    /// use ytesrev::{Point, Rect};
116    /// use ytesrev::drawable::Position;
117    ///
118    /// let tlc = Position::TopLeftCorner(Point::new(20, 50));
119    /// assert_eq!(tlc.into_rect_with_size(2, 7), Rect::new(20, 50, 2, 7));
120    ///
121    /// let center = Position::Center(Point::new(50, 10));
122    /// assert_eq!(center.into_rect_with_size(2, 8), Rect::new(49, 6, 2, 8));
123    ///
124    /// let rect = Position::Rect(Rect::new(10, 40, 20, 20));
125    /// assert_eq!(rect.into_rect_with_size(6, 4), Rect::new(17, 48, 6, 4));
126    ///
127    /// ```
128    pub fn into_rect_with_size_unbounded(self, width: u32, height: u32) -> Rect {
129        match self {
130            Position::TopLeftCorner(point) => Rect::new(point.x, point.y, width, height),
131            Position::Center(point) => Rect::new(
132                point.x - width as i32 / 2,
133                point.y - height as i32 / 2,
134                width,
135                height,
136            ),
137            Position::Rect(rect) => {
138                let center_x = rect.x() + rect.width() as i32 / 2;
139                let center_y = rect.y() + rect.height() as i32 / 2;
140
141                let x = (center_x - width as i32 / 2).max(rect.x);
142                let y = (center_y - height as i32 / 2).max(rect.y);
143
144                Rect::new(x, y, width, height)
145            }
146        }
147    }
148}
149
150/// An object that can be drawn
151pub trait Drawable: Send {
152    /// What this object contains
153    fn content(&self) -> Vec<&dyn Drawable>;
154    /// What this object contains, mutably
155    fn content_mut(&mut self) -> Vec<&mut dyn Drawable>;
156
157    /// Register all content. This is mostly just used by [`LatexObj`]s, that need to be
158    /// registered before loaded.
159    ///
160    /// [`LatexObj`]: ../latex/latex_obj/struct.LatexObj.rs
161    fn register(&mut self) {
162        for content in &mut self.content_mut() {
163            content.register();
164        }
165    }
166
167    /// Load all content
168    fn load(&mut self) {
169        for content in &mut self.content_mut() {
170            content.load();
171        }
172    }
173
174    /// When the user presses space, the state of the presentation is advanced. This
175    /// method is what is called.
176    fn step(&mut self);
177
178    /// When any event occurs
179    fn event(&mut self, e: Event) {
180        for x in self.content_mut() {
181            x.event(e.clone());
182        }
183    }
184
185    /// What state the object is in
186    fn state(&self) -> State;
187
188    /// Tick the object
189    fn update(&mut self, dt: f64) {
190        for content in &mut self.content_mut() {
191            content.update(dt);
192        }
193    }
194
195    /// Draw everything
196    fn draw(&self, _canvas: &mut Canvas<Window>, _position: &Position, _settings: DrawSettings);
197}
198
199/// An object that has a determined size, like an image, but not a solid that can fit any
200/// shape it's called with
201pub trait KnownSize: Drawable {
202    /// The width of the object
203
204    fn width(&self) -> usize;
205    /// The height of the object
206    fn height(&self) -> usize;
207}