diff options
Diffstat (limited to 'src/app.rs')
-rw-r--r-- | src/app.rs | 108 |
1 files changed, 98 insertions, 10 deletions
@@ -11,18 +11,27 @@ use sdl2::{ | |||
11 | mouse::MouseButton, | 11 | mouse::MouseButton, |
12 | pixels::Color, | 12 | pixels::Color, |
13 | rect::{Point, Rect}, | 13 | rect::{Point, Rect}, |
14 | render::Canvas, | 14 | render::{Canvas, Texture}, |
15 | ttf::Sdl2TtfContext, | ||
15 | video::Window, | 16 | video::Window, |
16 | Sdl, | 17 | Sdl, |
17 | }; | 18 | }; |
18 | 19 | ||
19 | use crate::consts::{BLACK, GRID_COLOR, WHITE}; | 20 | macro_rules! quick_rect( |
21 | ($x:expr, $y:expr, $w:expr, $h:expr) => ( | ||
22 | Rect::new($x as i32, $y as i32, $w as u32, $h as u32) | ||
23 | ) | ||
24 | ); | ||
25 | |||
26 | use crate::consts::{BLACK, FONT_PATH, GRID_COLOR, WHITE}; | ||
20 | 27 | ||
21 | pub struct AppState<'ctx> { | 28 | pub struct AppState<'ctx> { |
22 | active_color: bool, | 29 | active_color: bool, |
23 | brush_size: u8, | 30 | brush_size: u8, |
24 | canvas: Canvas<Window>, | 31 | canvas: Canvas<Window>, |
25 | context: &'ctx Sdl, | 32 | context: &'ctx Sdl, |
33 | ttf_context: &'ctx Sdl2TtfContext, | ||
34 | mouse: (i32, i32), | ||
26 | current_operation: Operation, | 35 | current_operation: Operation, |
27 | grid: Grid, | 36 | grid: Grid, |
28 | last_point: Option<Point>, | 37 | last_point: Option<Point>, |
@@ -261,13 +270,58 @@ impl<'ctx> AppState<'ctx> { | |||
261 | } | 270 | } |
262 | } | 271 | } |
263 | 272 | ||
273 | fn draw_statusline(&mut self) { | ||
274 | let (winsize_x, winsize_y) = self.canvas.window().size(); | ||
275 | let status_height: u32 = 20; | ||
276 | let status_width = winsize_x; | ||
277 | self.canvas.set_draw_color(WHITE); | ||
278 | self.canvas | ||
279 | .fill_rect(quick_rect!( | ||
280 | 0, | ||
281 | winsize_y - status_height, | ||
282 | status_width, | ||
283 | status_height | ||
284 | )) | ||
285 | .unwrap(); | ||
286 | let symmetry_symbol = match self.symmetry { | ||
287 | Symmetry { x: None, y: None } => "", | ||
288 | Symmetry { | ||
289 | x: Some(_), | ||
290 | y: None, | ||
291 | } => "-", | ||
292 | Symmetry { | ||
293 | x: None, | ||
294 | y: Some(_), | ||
295 | } => "|", | ||
296 | Symmetry { | ||
297 | x: Some(_), | ||
298 | y: Some(_), | ||
299 | } => "+", | ||
300 | }; | ||
301 | let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { | ||
302 | format!("{:3}, {:3}", x, y) | ||
303 | } else { | ||
304 | format!("--, --") | ||
305 | }; | ||
306 | let status_text = format!( | ||
307 | "[BRUSH {}] [SYM {:1}] {}", | ||
308 | self.brush_size + 1, | ||
309 | symmetry_symbol, | ||
310 | mouse_coords | ||
311 | ); | ||
312 | draw_text( | ||
313 | &mut self.canvas, | ||
314 | self.ttf_context, | ||
315 | status_text, | ||
316 | BLACK, | ||
317 | (0, winsize_y - status_height), | ||
318 | ); | ||
319 | } | ||
320 | |||
264 | fn draw(&mut self) { | 321 | fn draw(&mut self) { |
265 | let cs = self.zoom as u32; | 322 | let cs = self.zoom as u32; |
266 | let (width, height) = (self.width(), self.height()); | 323 | let (width, height) = (self.width(), self.height()); |
267 | let start = self.start; | 324 | let start = self.start; |
268 | if self.grid.enabled { | ||
269 | self.draw_grid(); | ||
270 | } | ||
271 | let canvas = &mut self.canvas; | 325 | let canvas = &mut self.canvas; |
272 | for (idx, val) in self.pixmap.data.iter().enumerate() { | 326 | for (idx, val) in self.pixmap.data.iter().enumerate() { |
273 | if *val { | 327 | if *val { |
@@ -277,15 +331,19 @@ impl<'ctx> AppState<'ctx> { | |||
277 | canvas | 331 | canvas |
278 | .fill_rect(Rect::new( | 332 | .fill_rect(Rect::new( |
279 | // start drawing 1 pixel after the grid line | 333 | // start drawing 1 pixel after the grid line |
280 | x * cs as i32 + start.x() + 1, | 334 | x * cs as i32 + start.x(), |
281 | y * cs as i32 + start.y() + 1, | 335 | y * cs as i32 + start.y(), |
282 | // stop drawing 1 pixel before the grid line | 336 | // stop drawing 1 pixel before the grid line |
283 | cs - 1, | 337 | cs, |
284 | cs - 1, | 338 | cs, |
285 | )) | 339 | )) |
286 | .unwrap(); | 340 | .unwrap(); |
287 | } | 341 | } |
288 | } | 342 | } |
343 | if self.grid.enabled { | ||
344 | self.draw_grid(); | ||
345 | } | ||
346 | self.draw_statusline(); | ||
289 | } | 347 | } |
290 | 348 | ||
291 | fn redraw(&mut self) { | 349 | fn redraw(&mut self) { |
@@ -298,12 +356,18 @@ impl<'ctx> AppState<'ctx> { | |||
298 | 356 | ||
299 | // publicly available functions on appstate | 357 | // publicly available functions on appstate |
300 | impl<'ctx> AppState<'ctx> { | 358 | impl<'ctx> AppState<'ctx> { |
301 | pub fn init(width: u32, height: u32, context: &'ctx Sdl) -> Self { | 359 | pub fn init( |
360 | width: u32, | ||
361 | height: u32, | ||
362 | context: &'ctx Sdl, | ||
363 | ttf_context: &'ctx Sdl2TtfContext, | ||
364 | ) -> Self { | ||
302 | let video_subsystem = context.video().unwrap(); | 365 | let video_subsystem = context.video().unwrap(); |
303 | 366 | ||
304 | let window = video_subsystem | 367 | let window = video_subsystem |
305 | .window("Pixel editor", 200, 200) | 368 | .window("Pixel editor", 200, 200) |
306 | .position_centered() | 369 | .position_centered() |
370 | .resizable() | ||
307 | .opengl() | 371 | .opengl() |
308 | .build() | 372 | .build() |
309 | .map_err(|e| e.to_string()) | 373 | .map_err(|e| e.to_string()) |
@@ -321,6 +385,8 @@ impl<'ctx> AppState<'ctx> { | |||
321 | brush_size: 0, | 385 | brush_size: 0, |
322 | canvas, | 386 | canvas, |
323 | context, | 387 | context, |
388 | ttf_context, | ||
389 | mouse: (0, 0), | ||
324 | current_operation: Vec::new(), | 390 | current_operation: Vec::new(), |
325 | grid: Grid::new(), | 391 | grid: Grid::new(), |
326 | last_point: None, | 392 | last_point: None, |
@@ -341,6 +407,7 @@ impl<'ctx> AppState<'ctx> { | |||
341 | let mut event_pump = self.context.event_pump().unwrap(); | 407 | let mut event_pump = self.context.event_pump().unwrap(); |
342 | 'running: loop { | 408 | 'running: loop { |
343 | let mouse = event_pump.mouse_state(); | 409 | let mouse = event_pump.mouse_state(); |
410 | self.mouse = (mouse.x(), mouse.y()); | ||
344 | for event in event_pump.poll_iter() { | 411 | for event in event_pump.poll_iter() { |
345 | match event { | 412 | match event { |
346 | Event::KeyDown { | 413 | Event::KeyDown { |
@@ -449,3 +516,24 @@ impl<'ctx> AppState<'ctx> { | |||
449 | } | 516 | } |
450 | } | 517 | } |
451 | } | 518 | } |
519 | |||
520 | fn draw_text<S: AsRef<str>>( | ||
521 | canvas: &mut Canvas<Window>, | ||
522 | ttf_context: &Sdl2TtfContext, | ||
523 | text: S, | ||
524 | color: Color, | ||
525 | (x, y): (u32, u32), | ||
526 | ) { | ||
527 | let text = text.as_ref(); | ||
528 | let texture_creator = canvas.texture_creator(); | ||
529 | let mut font = ttf_context.load_font(FONT_PATH, 17).unwrap(); | ||
530 | font.set_style(sdl2::ttf::FontStyle::NORMAL); | ||
531 | font.set_hinting(sdl2::ttf::Hinting::Mono); | ||
532 | let surface = font.render(text.as_ref()).blended(color).unwrap(); | ||
533 | let texture = texture_creator | ||
534 | .create_texture_from_surface(&surface) | ||
535 | .unwrap(); | ||
536 | let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); | ||
537 | let area = quick_rect!(x, y, width, height); | ||
538 | canvas.copy(&texture, None, area); | ||
539 | } | ||