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}