From f55860b3c5da45c6d7a208d638348557d05468fa Mon Sep 17 00:00:00 2001 From: Huck Boles Date: Sun, 28 May 2023 18:18:51 -0500 Subject: [PATCH] added: moveable cursor --- src/cells.rs | 15 ++++- src/graphics.rs | 33 +++++++---- src/graphics/map.rs | 20 ++++++- src/graphics/point.rs | 6 ++ src/graphics/selector.rs | 115 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 7 ++- 6 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 src/graphics/selector.rs diff --git a/src/cells.rs b/src/cells.rs index c0fceb8..9f24fcb 100644 --- a/src/cells.rs +++ b/src/cells.rs @@ -1,4 +1,5 @@ use crate::{Map, Mask, Point}; +use crossterm::style::{Attribute, Color}; use ndarray::Array2; use rand::{thread_rng, Rng}; use std::{cell::Cell, ops::Deref}; @@ -84,10 +85,22 @@ impl Map> for World { self.nrows() } - fn graphics(&self) -> (char, char) { + fn characters(&self) -> (char, char) { ('●', '◌') } + fn fg_colors(&self) -> (Color, Color) { + (Color::Green, Color::Grey) + } + + fn bg_colors(&self) -> (Color, Color) { + (Color::Black, Color::Black) + } + + fn styles(&self) -> (Attribute, Attribute) { + (Attribute::Bold, Attribute::Reset) + } + fn try_point(&self, point: Point) -> bool { if let Some(cell) = self.get((point.y, point.x)) { if cell.get() == State::Alive { diff --git a/src/graphics.rs b/src/graphics.rs index 44fc0dd..a5de1b1 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,14 +1,13 @@ mod map; mod point; +mod selector; pub use map::*; pub use point::*; +pub use selector::*; -use crossterm::{ - cursor, execute, queue, - style::{self, Stylize}, - terminal, Result, -}; +use crossterm::{cursor, execute, queue, style, terminal}; +use eyre::Result; use std::{ io::{stdout, Write}, thread, @@ -18,31 +17,41 @@ use std::{ pub struct Cursor {} pub fn draw_frame(map: &mut impl Map, offset: Point) -> Result<()> { - let (on, off) = map.graphics(); + let (char_on, char_off) = map.characters(); + let (fg_on, fg_off) = map.fg_colors(); + let (bg_on, bg_off) = map.bg_colors(); + let (style_on, style_off) = map.styles(); + for x in 1..(map.x_size() - 1) { for y in 1..(map.y_size() - 1) { let point = Point::new(x, y); - let offset = point + offset; - let x_off: u16 = offset.x.try_into().unwrap(); - let y_off: u16 = offset.y.try_into().unwrap(); + let (x_off, y_off) = point.u16_offset(offset)?; + if map.try_point(point) { queue!( stdout(), cursor::Hide, cursor::MoveTo(x_off, y_off), - style::PrintStyledContent(on.green()) + style::SetAttribute(style_on), + style::SetForegroundColor(fg_on), + style::SetBackgroundColor(bg_on), + style::Print(char_on) )?; } else { queue!( stdout(), cursor::Hide, cursor::MoveTo(x_off, y_off), - style::PrintStyledContent(off.grey()) + style::SetAttribute(style_off), + style::SetForegroundColor(fg_off), + style::SetBackgroundColor(bg_off), + style::Print(char_off) )?; } } } - stdout().flush() + stdout().flush()?; + Ok(()) } pub fn run_map(map: &mut impl Map, offset: Point, time: Duration) -> Result<()> { diff --git a/src/graphics/map.rs b/src/graphics/map.rs index a003edc..6748e15 100644 --- a/src/graphics/map.rs +++ b/src/graphics/map.rs @@ -1,4 +1,5 @@ use crate::Point; +use crossterm::style::{Attribute, Color}; use ndarray::Array2; use std::ops::Deref; @@ -7,7 +8,10 @@ pub trait Map { fn get_point(&self, point: Point) -> Option; fn x_size(&self) -> usize; fn y_size(&self) -> usize; - fn graphics(&self) -> (char, char); + fn characters(&self) -> (char, char); + fn fg_colors(&self) -> (Color, Color); + fn bg_colors(&self) -> (Color, Color); + fn styles(&self) -> (Attribute, Attribute); fn update(&mut self); } @@ -47,9 +51,21 @@ impl Map for Mask { self.get((point.y, point.x)).cloned() } - fn graphics(&self) -> (char, char) { + fn characters(&self) -> (char, char) { ('■', '□') } + fn fg_colors(&self) -> (Color, Color) { + (Color::White, Color::Grey) + } + + fn bg_colors(&self) -> (Color, Color) { + (Color::Black, Color::Black) + } + + fn styles(&self) -> (Attribute, Attribute) { + (Attribute::Bold, Attribute::Reset) + } + fn update(&mut self) {} } diff --git a/src/graphics/point.rs b/src/graphics/point.rs index 2585d35..3af27b3 100644 --- a/src/graphics/point.rs +++ b/src/graphics/point.rs @@ -1,4 +1,5 @@ use crossterm::style::{Attribute, Color}; +use eyre::Result; use std::ops::{Add, Div, Mul, Sub}; pub struct Pixel { @@ -19,6 +20,11 @@ impl Point { pub fn new(x: usize, y: usize) -> Self { Self { x, y } } + + pub fn u16_offset(&self, offset: Point) -> Result<(u16, u16)> { + let output = *self + offset; + Ok((output.x.try_into()?, output.y.try_into()?)) + } } impl Add for Point { diff --git a/src/graphics/selector.rs b/src/graphics/selector.rs new file mode 100644 index 0000000..51298d4 --- /dev/null +++ b/src/graphics/selector.rs @@ -0,0 +1,115 @@ +use crate::Point; +use crossterm::{ + cursor, + event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers}, + execute, queue, + style::{self, Attribute, Color}, + terminal, +}; +use eyre::Result; +use std::{ + io::stdout, + ops::{Deref, DerefMut}, + time::Duration, +}; + +pub struct Cursor { + pub position: Point, + pub fg: Color, + pub bg: Color, + pub style: Attribute, + pub char: char, +} + +impl Deref for Cursor { + type Target = Point; + fn deref(&self) -> &Self::Target { + &self.position + } +} + +impl DerefMut for Cursor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.position + } +} + +impl Cursor { + pub fn new() -> Self { + Self { + position: Point::new(0, 0), + fg: Color::White, + bg: Color::Black, + char: '*', + style: Attribute::Reset, + } + } + + pub fn render(&self) -> Result<()> { + queue!( + stdout(), + cursor::Hide, + cursor::MoveTo(self.x.try_into().unwrap(), self.y.try_into().unwrap()), + style::SetAttribute(self.style), + style::SetForegroundColor(self.fg), + style::SetBackgroundColor(self.bg), + style::Print(self.char.to_string()) + )?; + Ok(()) + } + + pub fn update(&mut self, event: KeyEvent) -> Result<()> { + let key = event.code; + match event.modifiers { + KeyModifiers::NONE => match key { + KeyCode::Char(c) => match c { + 'j' => self.y += 1, + 'k' => { + if self.y != 0 { + self.y -= 1 + } + } + 'h' => { + if self.x != 0 { + self.x -= 1 + } + } + 'l' => self.x += 1, + _ => {} + }, + _ => {} + }, + KeyModifiers::CONTROL => match key { + KeyCode::Char(c) => match c { + 'c' => std::process::exit(0), + _ => {} + }, + _ => {} + }, + KeyModifiers::ALT => match key { + _ => {} + }, + KeyModifiers::SHIFT => match key { + _ => {} + }, + _ => {} + }; + Ok(()) + } +} + +pub fn move_cursor() -> Result<()> { + let mut cursor = Cursor::new(); + loop { + if poll(Duration::from_millis(10))? { + execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; + match read()? { + Event::Key(event) => { + cursor.update(event)?; + } + _ => continue, + } + } + cursor.render()?; + } +} diff --git a/src/main.rs b/src/main.rs index fe995fb..069843a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,13 +2,16 @@ use cellseq::*; use eyre::Result; fn main() -> Result<()> { + crossterm::terminal::enable_raw_mode()?; + let mut mask: Mask = Mask::new(48, 24, false); - let mut map = World::new(48, 24); + let mut map = World::new(74, 32); map.randomize(0.75); let time = bpm_to_ms(120); - loop_map(&mut map, Point::new(10, 5), time, 32)?; + // loop_map(&mut map, Point::new(10, 2), time, 32)?; // edit_mask(&mask, (10, 5))?; + move_cursor()?; Ok(()) } -- 2.45.2