From 9844d702a1f33ca04815166f539616a519a89cac Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 14 Mar 2021 22:45:46 +0530 Subject: add basic statusline widget; text drawing support --- src/app.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/consts.rs | 1 + src/main.rs | 3 +- 3 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/app.rs b/src/app.rs index 13f8732..695d097 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,18 +11,27 @@ use sdl2::{ mouse::MouseButton, pixels::Color, rect::{Point, Rect}, - render::Canvas, + render::{Canvas, Texture}, + ttf::Sdl2TtfContext, video::Window, Sdl, }; -use crate::consts::{BLACK, GRID_COLOR, WHITE}; +macro_rules! quick_rect( + ($x:expr, $y:expr, $w:expr, $h:expr) => ( + Rect::new($x as i32, $y as i32, $w as u32, $h as u32) + ) +); + +use crate::consts::{BLACK, FONT_PATH, GRID_COLOR, WHITE}; pub struct AppState<'ctx> { active_color: bool, brush_size: u8, canvas: Canvas, context: &'ctx Sdl, + ttf_context: &'ctx Sdl2TtfContext, + mouse: (i32, i32), current_operation: Operation, grid: Grid, last_point: Option, @@ -261,13 +270,58 @@ impl<'ctx> AppState<'ctx> { } } + fn draw_statusline(&mut self) { + let (winsize_x, winsize_y) = self.canvas.window().size(); + let status_height: u32 = 20; + let status_width = winsize_x; + self.canvas.set_draw_color(WHITE); + self.canvas + .fill_rect(quick_rect!( + 0, + winsize_y - status_height, + status_width, + status_height + )) + .unwrap(); + let symmetry_symbol = match self.symmetry { + Symmetry { x: None, y: None } => "", + Symmetry { + x: Some(_), + y: None, + } => "-", + Symmetry { + x: None, + y: Some(_), + } => "|", + Symmetry { + x: Some(_), + y: Some(_), + } => "+", + }; + let mouse_coords = if let Some((x, y)) = self.idx_at_coord(self.mouse) { + format!("{:3}, {:3}", x, y) + } else { + format!("--, --") + }; + let status_text = format!( + "[BRUSH {}] [SYM {:1}] {}", + self.brush_size + 1, + symmetry_symbol, + mouse_coords + ); + draw_text( + &mut self.canvas, + self.ttf_context, + status_text, + BLACK, + (0, winsize_y - status_height), + ); + } + fn draw(&mut self) { let cs = self.zoom as u32; let (width, height) = (self.width(), self.height()); let start = self.start; - if self.grid.enabled { - self.draw_grid(); - } let canvas = &mut self.canvas; for (idx, val) in self.pixmap.data.iter().enumerate() { if *val { @@ -277,15 +331,19 @@ impl<'ctx> AppState<'ctx> { canvas .fill_rect(Rect::new( // start drawing 1 pixel after the grid line - x * cs as i32 + start.x() + 1, - y * cs as i32 + start.y() + 1, + x * cs as i32 + start.x(), + y * cs as i32 + start.y(), // stop drawing 1 pixel before the grid line - cs - 1, - cs - 1, + cs, + cs, )) .unwrap(); } } + if self.grid.enabled { + self.draw_grid(); + } + self.draw_statusline(); } fn redraw(&mut self) { @@ -298,12 +356,18 @@ impl<'ctx> AppState<'ctx> { // publicly available functions on appstate impl<'ctx> AppState<'ctx> { - pub fn init(width: u32, height: u32, context: &'ctx Sdl) -> Self { + pub fn init( + width: u32, + height: u32, + context: &'ctx Sdl, + ttf_context: &'ctx Sdl2TtfContext, + ) -> Self { let video_subsystem = context.video().unwrap(); let window = video_subsystem .window("Pixel editor", 200, 200) .position_centered() + .resizable() .opengl() .build() .map_err(|e| e.to_string()) @@ -321,6 +385,8 @@ impl<'ctx> AppState<'ctx> { brush_size: 0, canvas, context, + ttf_context, + mouse: (0, 0), current_operation: Vec::new(), grid: Grid::new(), last_point: None, @@ -341,6 +407,7 @@ impl<'ctx> AppState<'ctx> { let mut event_pump = self.context.event_pump().unwrap(); 'running: loop { let mouse = event_pump.mouse_state(); + self.mouse = (mouse.x(), mouse.y()); for event in event_pump.poll_iter() { match event { Event::KeyDown { @@ -449,3 +516,24 @@ impl<'ctx> AppState<'ctx> { } } } + +fn draw_text>( + canvas: &mut Canvas, + ttf_context: &Sdl2TtfContext, + text: S, + color: Color, + (x, y): (u32, u32), +) { + let text = text.as_ref(); + let texture_creator = canvas.texture_creator(); + let mut font = ttf_context.load_font(FONT_PATH, 17).unwrap(); + font.set_style(sdl2::ttf::FontStyle::NORMAL); + font.set_hinting(sdl2::ttf::Hinting::Mono); + let surface = font.render(text.as_ref()).blended(color).unwrap(); + let texture = texture_creator + .create_texture_from_surface(&surface) + .unwrap(); + let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); + let area = quick_rect!(x, y, width, height); + canvas.copy(&texture, None, area); +} diff --git a/src/consts.rs b/src/consts.rs index b5cea43..8c90693 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,3 +4,4 @@ pub const GRID_COLOR: Color = Color::RGB(64, 64, 64); pub const WHITE: Color = Color::RGB(255, 255, 255); pub const BLACK: Color = Color::RGB(0, 0, 0); +pub const FONT_PATH: &'static str = "./assets/NerdInput-Regular.ttf"; diff --git a/src/main.rs b/src/main.rs index cb39648..bd0590e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,5 +7,6 @@ use app::AppState; pub fn main() { let sdl_context = sdl2::init().unwrap(); - AppState::init(100, 100, &sdl_context).run(); + let ttf_context = sdl2::ttf::init().unwrap(); + AppState::init(100, 100, &sdl_context, &ttf_context).run(); } -- cgit v1.2.3