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