diff --git a/src/system/std/frame.rs b/src/system/std/frame.rs index 25a2455..56e7ce7 100644 --- a/src/system/std/frame.rs +++ b/src/system/std/frame.rs @@ -1,7 +1,7 @@ use alloc::string::String; use alloc::vec; use alloc::vec::Vec; -use crate::system::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER, ScreenChar}; +use crate::system::kernel::render::{RENDERER, ScreenChar}; use crate::std::io::{Color, Screen}; /// TODO: get a working implementation for CLI apps @@ -12,7 +12,13 @@ use crate::std::io::{Color, Screen}; /// nothing will appear on the screen until the frame is actually rendered by /// the write_to_screen() method on the renderer -pub use crate::system::kernel::render::{special_char, RenderError, ColorCode}; +pub use crate::system::kernel::render::{ + special_char, + RenderError, + ColorCode, + BUFFER_WIDTH, + BUFFER_HEIGHT +}; #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/src/user/bin/mod.rs b/src/user/bin/mod.rs index 77cfef3..b809b9c 100644 --- a/src/user/bin/mod.rs +++ b/src/user/bin/mod.rs @@ -12,3 +12,4 @@ mod gameoflife; mod tetris; mod asteroids; mod crystalrpg; +mod pong; diff --git a/src/user/bin/pong.rs b/src/user/bin/pong.rs new file mode 100644 index 0000000..152f42f --- /dev/null +++ b/src/user/bin/pong.rs @@ -0,0 +1,57 @@ +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use async_trait::async_trait; +use core::fmt::Write; +use crate::std::application::{Application, Error}; +use crate::std; + +struct Game { + ball: Ball, + player1: Player, + player2: Player, +} + +#[async_trait] +impl Application for Game { + fn new() -> Self { + Game { + ball: Ball::new(), + player1: Player::new(1), + player2: Player::new(2), + } + } + + async fn run(&mut self, _: Vec) -> Result<(), Error> { + loop { + self.ball.update(&self.player1, &self.player2); + } + Ok(()) + } +} + +struct Player { + x: i32, + y: i32, + score: i32, +} + +impl Player { + fn new(y: i32) -> Self { + Player { x: 0, y, score: 0 } + } +} + +struct Ball { + x: i32, + y: i32, +} + +impl Ball { + fn new() -> Self { + Ball { x: 0, y: 0 } + } + fn update(&mut self, player1: &Player, player2: &Player) { + self.x += 1; + } +} diff --git a/src/user/bin/shell.rs b/src/user/bin/shell.rs index 00fa655..f916302 100644 --- a/src/user/bin/shell.rs +++ b/src/user/bin/shell.rs @@ -18,6 +18,7 @@ use crate::user::lib::libgui::{ cg_inputs::CgLineEdit, }; use crate::user::lib::libgui::cg_core::{CgTextInput, Widget}; +use crate::user::lib::libgui::cg_widgets::CgDialog; lazy_static! { pub static ref CMD: Mutex = Mutex::new(CommandHandler::new()); @@ -253,6 +254,18 @@ struct CmdHistory { } async fn setup_ui() { + let dialog = CgDialog::new( + Dimensions::new(40, 10), + String::from("test dialog"), + String::from("dialog body"), + String::from("[dialog footer]") + ); + + if let Ok(frame) = dialog.render() { + frame.write_to_screen().unwrap(); + } + return; + serial_println!("idk"); let label= Widget::insert(CgLabel::new( String::from("test label"), diff --git a/src/user/lib/libgui/cg_core.rs b/src/user/lib/libgui/cg_core.rs index 228adf7..bc0cea2 100644 --- a/src/user/lib/libgui/cg_core.rs +++ b/src/user/lib/libgui/cg_core.rs @@ -29,7 +29,6 @@ pub trait CgComponent: Any { fn as_any(&self) -> &dyn Any; } - /// trait for components that can have editable text, such as search boxes, command palettes, terminals, text inputs etc. pub trait CgTextEdit: CgComponent { fn write_char(&mut self, c: char); // this can also be implemented in a way that inserts characters @@ -43,7 +42,10 @@ pub trait CgTextInput: CgTextEdit { async fn input(&mut self, break_condition: fn(KeyStroke) -> (KeyStroke, Exit), id: &Widget, app: &Widget) -> Result<(String, bool), RenderError>; } - +#[async_trait] +pub trait CgKeyboardCapture: CgComponent { + async fn keyboard_capture(&mut self, break_condition: fn(KeyStroke) -> (KeyStroke, Exit), app: Option<&Widget>) -> Result; +} static ID_COUNTER: Mutex = Mutex::new(0); diff --git a/src/user/lib/libgui/cg_utils.rs b/src/user/lib/libgui/cg_utils.rs new file mode 100644 index 0000000..948f479 --- /dev/null +++ b/src/user/lib/libgui/cg_utils.rs @@ -0,0 +1,21 @@ +use crate::std::frame::{ColouredChar, Dimensions, Frame, Position}; + +pub(crate) fn render_outline(frame: &mut Frame, dimensions: Dimensions) { + // draws the sides of the container + for i in 0..frame.dimensions.x { + frame.write(Position::new(i, 0), ColouredChar::new('─')); + frame.write(Position::new(i, frame.dimensions.y - 1), ColouredChar::new('─')); + } + + // draws the top and bottom of the container + for i in 0..frame.dimensions.y { + frame.write(Position::new(0, i), ColouredChar::new('│')); + frame.write(Position::new(frame.dimensions.x - 1, i), ColouredChar::new('│')); + } + + // draws the corners of the container + frame.write(Position::new(0, 0), ColouredChar::new('┌')); + frame.write(Position::new(dimensions.x - 1, 0), ColouredChar::new('┐')); + frame.write(Position::new(0, dimensions.y - 1), ColouredChar::new('└')); + frame.write(Position::new(dimensions.x - 1, dimensions.y - 1), ColouredChar::new('┘')); +} \ No newline at end of file diff --git a/src/user/lib/libgui/cg_widgets.rs b/src/user/lib/libgui/cg_widgets.rs index 7562ca0..a897350 100644 --- a/src/user/lib/libgui/cg_widgets.rs +++ b/src/user/lib/libgui/cg_widgets.rs @@ -6,7 +6,8 @@ use core::cmp::{max, min}; use hashbrown::HashMap; use crate::serial_println; use super::cg_core::{CgComponent, CgOutline, Widget}; -use crate::std::frame::{ColouredChar, Dimensions, Position, Frame, RenderError, ColorCode}; +use super::cg_utils::render_outline; +use crate::std::frame::{ColouredChar, Dimensions, Position, Frame, RenderError, ColorCode, BUFFER_WIDTH, BUFFER_HEIGHT}; use crate::std::io::Color; #[derive(Debug, Clone)] @@ -33,29 +34,6 @@ impl CgContainer { self.elements.get(name) } } - -impl CgOutline for CgContainer { - fn render_outline(&self, frame: &mut Frame) { - // draws the sides of the container - for i in 0..frame.dimensions.x { - frame.write(Position::new(i, 0), ColouredChar::new('─')); - frame.write(Position::new(i, frame.dimensions.y - 1), ColouredChar::new('─')); - } - - // draws the top and bottom of the container - for i in 0..frame.dimensions.y { - frame.write(Position::new(0, i), ColouredChar::new('│')); - frame.write(Position::new(frame.dimensions.x - 1, i), ColouredChar::new('│')); - } - - // draws the corners of the container - frame.write(Position::new(0, 0), ColouredChar::new('┌')); - frame.write(Position::new(self.dimensions.x - 1, 0), ColouredChar::new('┐')); - frame.write(Position::new(0, self.dimensions.y - 1), ColouredChar::new('└')); - frame.write(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::new('┘')); - } -} - impl CgComponent for CgContainer { fn render(&self) -> Result { let mut result = Frame::new(self.position, self.dimensions)?; @@ -68,7 +46,7 @@ impl CgComponent for CgContainer { } if self.outlined { - self.render_outline(&mut result); + render_outline(&mut result, self.dimensions.clone()); } Ok(result) @@ -115,7 +93,7 @@ impl CgComponent for CgTextBox { let mut result = Frame::new(self.position, self.dimensions)?; if self.outlined { - self.render_outline(&mut result); + render_outline(&mut result, self.dimensions.clone()); } self.render_title(&mut result); @@ -161,32 +139,6 @@ impl CgComponent for CgTextBox { } } - -impl CgOutline for CgTextBox { - fn render_outline(&self, frame: &mut Frame) { - // draws the sides of the container - for i in 0..frame.dimensions.x { - frame.write(Position::new(i, 0), ColouredChar::new('─')); - frame.write(Position::new(i, frame.dimensions.y - 1), ColouredChar::new('─')); - } - - // draws the top and bottom of the container - for i in 0..frame.dimensions.y { - frame.write(Position::new(0, i), ColouredChar::new('│')); - frame.write(Position::new(frame.dimensions.x - 1, i), ColouredChar::new('│')); - } - - // draws the corners of the container - frame.write(Position::new(0, 0), ColouredChar::new('┌')); - frame.write(Position::new(self.dimensions.x - 1, 0), ColouredChar::new('┐')); - frame.write(Position::new(0, self.dimensions.y - 1), ColouredChar::new('└')); - frame.write(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::new('┘')); - } -} - - - - #[derive(Debug, Clone)] pub struct CgLabel { content: String, @@ -349,7 +301,7 @@ impl CgComponent for CgStatusBar { fn render(&self) -> Result { let mut frame = Frame::new(self.position, self.dimensions)?; - (0..80).for_each(|x| frame[0][x] = ColouredChar::coloured(' ', ColorCode::new(Color::Black, Color::DarkGray))); + (0..BUFFER_WIDTH).for_each(|x| frame[0][x] = ColouredChar::coloured(' ', ColorCode::new(Color::Black, Color::DarkGray))); // render window title centred let mut window_title = self.window_title.render()?; @@ -393,10 +345,54 @@ impl CgStatusBar { } } - - - - +enum CgDialogType { + Information, + Confirmation, +} + +pub struct CgDialog { + dimensions: Dimensions, + title: String, + content: String, + button_text: String, + accepted: bool, + outlined: bool, +} + +impl CgDialog { + pub(crate) fn new(dimensions: Dimensions, title: String, content: String, button_text: String) -> CgDialog { + CgDialog { + dimensions, + title, + content, + button_text, + accepted: false, + outlined: true, + } + } +} + +impl CgComponent for CgDialog { + fn render(&self) -> Result { + if self.dimensions.x > BUFFER_WIDTH || self.dimensions.y > BUFFER_HEIGHT { + return Err(RenderError::OutOfBounds(self.dimensions.x > BUFFER_WIDTH, self.dimensions.y > BUFFER_HEIGHT)); + } + let x_offset = (BUFFER_WIDTH - self.dimensions.x) / 2; + let y_offset = (BUFFER_HEIGHT - self.dimensions.y) / 2; + + let mut frame = Frame::new(Position::new(x_offset, y_offset), Dimensions::new(self.dimensions.x, self.dimensions.y))?; + + if self.outlined { + render_outline(&mut frame, self.dimensions); + } + + Ok(frame) + } + + fn as_any(&self) -> &dyn Any { + self + } +} diff --git a/src/user/lib/libgui/mod.rs b/src/user/lib/libgui/mod.rs index 333a3bc..4f4d408 100644 --- a/src/user/lib/libgui/mod.rs +++ b/src/user/lib/libgui/mod.rs @@ -1,4 +1,5 @@ pub mod cg_core; pub mod cg_widgets; pub mod cg_inputs; +mod cg_utils; diff --git a/src/user/lib/libgui_old_archive/libgui_core.rs b/src/user/lib/libgui_old_archive/libgui_core.rs deleted file mode 100644 index 458eb82..0000000 --- a/src/user/lib/libgui_old_archive/libgui_core.rs +++ /dev/null @@ -1,287 +0,0 @@ -use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER, ScreenChar}; -use crate::system::std::frame::Frame; -use crate::{print, println}; -use alloc::{ - boxed::Box, - string::{String, ToString}, - vec, - vec::Vec, -}; -/* - -- this library will provide useful structures for creating simple - command line based interfaces using ascii - -*/ - -#[derive(Copy, Clone)] -pub struct Pos { - pub x: usize, - pub y: usize, -} -impl Pos { - pub fn new(x: usize, y: usize) -> Pos { - Pos { x, y } - } -} - -/// all interface elements must implement this trait in order to be -/// rendered on the screen -pub trait Element { - // default behaviour for all elements - - fn render(&self) -> (Vec>, Pos) { - // recursive method for rendering the - // specified frame to the screen - // insert rendering code for specific frame here - // this should also render all children of the element - (Vec::>::new(), Pos::new(0, 0)) - } -} - -pub struct Container<'a> { - // a simple container objects for grouping - // other containers together - frame: Vec>, - elements: Vec>, - position: Pos, // x,y - // - outlined: bool, - dimensions: Pos, // x,y -} - -impl<'a> Container<'a> { - fn new(position: Pos, dimensions: Pos, outlined: bool) -> Container<'a> { - Self { - frame: vec![vec![' '; dimensions.x as usize]; dimensions.y as usize], - elements: Vec::new(), - position, - outlined, - dimensions, - } - } -} - -impl Element for Container<'_> { - fn render(&self) -> (Vec>, Pos) { - // returns all elements as a single frame - - let mut charmap = Vec::>::new(); - - let mut frstline: Vec; - let mut midlines: Vec; - let mut lastline: Vec; - - if self.outlined { - charmap = gen_outline(self.dimensions); - } - - // render child elements - - for element in &self.elements { - let r = (*element).render(); - - // rendering code for child elements goes here - - // code to render the object at the position marked by offset within the container - - for (i, row) in r.0.iter().enumerate() { - for (j, chr) in row.iter().enumerate() { - // r.0 is the rendered element - // r.1.0 is the x offset - charmap[i + r.1.y][j + r.1.x] = *chr; // r.1.1 is the y offset - } - } - } - - return (charmap, self.position); - } -} - -pub struct IndicatorBar { - length: usize, - filled: usize, - abs: usize, - position: Pos, - text: Option, -} - -impl IndicatorBar { - fn new(position: Pos, length: usize) -> IndicatorBar { - IndicatorBar { - position, - length, - abs: 0, - filled: 0, - text: None, - } - } - fn set_value(&mut self, value: usize) { - // takes a value from 1-100% - // and turns it into a corresponding length filled - self.filled = value; - } - fn set_text(&mut self, s: String) { - self.text = Some(s); - } -} - -impl Element for IndicatorBar { - fn render(&self) -> (Vec>, Pos) { - let numlen = (self.abs.to_string().as_str()).len(); - let relfilled = (self.filled as f64 / 100.0 * ((self.length - numlen) as f64)) as usize; - - let mut line = Vec::::new(); - if let Some(t) = &self.text { - line.append(&mut t.chars().collect()); - line.push(':'); - line.push(' '); - } - line.append(&mut (self.abs.to_string().chars().collect())); - line.append(&mut vec!['▓'; relfilled]); - line.append(&mut vec!['░'; self.length - numlen - relfilled]); - - let mut rendered = Vec::new(); - rendered.push(line); - - println!("RENDERED: {:?}", rendered); - - return (rendered, (self.position)); - } -} - -// functions that deal with the rendering and interaction between objects being -// rendered. - -pub fn render_frame(elements: Vec) { - let mut buffer: Frame = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; - - for frame in elements.iter() { - let f = frame.render(); - - for (i, row) in f.0.iter().enumerate() { - for (j, chr) in row.iter().enumerate() { - let mut current = &buffer[i + f.1.y][j + f.1.x]; - let newchar = overlap_check(current.character as char, *chr); - buffer[i + f.1.y][j + f.1.x] = ScreenChar::white(newchar as u8); - - //print!("{}", buffer[i+frame.position.1][j+frame.position.0]); - } - } - } - - //println!("{:?}", buffer); - - RENDERER.lock().render_frame(buffer) -} - -pub fn overlap_check(oldchar: char, newchar: char) -> char { - match (oldchar, newchar) { - //┌│└ ┐┘─ - ('│', '─') | ('┌', '┘') | ('└', '┐') => '┼', - ('┌', '└') => '├', - ('┐', '┐') => '┤', - - (_, _) => newchar, - } -} - -// function to return a charmap of the outline of an object - -pub fn gen_outline(dimensions: Pos) -> Vec> { - let mut charmap = Vec::>::new(); - - let mut frstline = vec!['┌']; - let mut midlines = vec!['│']; - let mut lastline = vec!['└']; - - frstline.append(&mut vec!['─'; dimensions.x - 2]); - midlines.append(&mut vec![' '; dimensions.x - 2]); - lastline.append(&mut vec!['─'; dimensions.x - 2]); - - frstline.append(&mut vec!['┐']); - midlines.append(&mut vec!['│']); - lastline.append(&mut vec!['┘']); - - charmap.push(frstline); - for _ in 0..dimensions.y - 2 { - charmap.push(midlines.clone()); - } - charmap.push(lastline); - - return charmap; -} - -// // testing functions -// -// pub fn test_elements() { -// use super::libgui_elements; -// -// let mut containers = Vec::::new(); -// -// /* -// -// //for _ in 0..10 { -// // containers.push(generate_box()); -// //} -// -// containers.push(Container::new((5, 5), (15, 5), true)); -// containers.push(Container::new((10, 3), (50, 20), true)); -// -// let mut bar = IndicatorBar::new((10, 6), 12); -// let mut bar2 = IndicatorBar::new((10, 7), 12); -// -// bar.set_value(43); -// bar.abs = 101; -// bar2.set_value(14); -// bar2.abs = 15; -// containers[1].elements.push(Box::new(bar)); -// containers[1].elements.push(Box::new(bar2)); -// -// let tbox = libgui_elements::TextBox::new( -// String::from("panic attack simps"), -// String::from("i have finally obtained evidence of his simpiness against tari and crystal, however i cannot reveal this evidence for now, however, once the contract is over NO ONE CAN STOP ME MWHAHAHAHAHA"), -// Pos::new(25, 10), -// Pos::new(10, 9), -// true, -// ); -// -// containers[1].elements.push(Box::new(tbox)); -// -// */ -// -// containers.push(Container::new(Pos::new(0, 1), Pos::new(80, 24), true)); -// -// let tbox = libgui_elements::TextBox::new( -// String::from("ANNOUNCEMENTS"), -// String::from( -// "CrystalRPG coming soon! XD -// this is gonna be the best game ever", -// ), -// Pos::new(25, 10), -// Pos::new(0, 0), -// true, -// ); -// -// containers[0].elements.push(Box::new(&tbox)); -// -// let mut bar = IndicatorBar::new(Pos::new(7, 7), 12); -// bar.set_value(70); -// bar.set_text(String::from("ayo")); -// -// containers[0].elements.push(Box::new(bar)); -// render_frame(containers); -// -// return; -// } - -// function to generate a box in a random location on the screen. - -fn generate_box() -> Container<'static> { - use crate::std::random::Random; - let width = Random::int(5, 20); - let height = Random::int(5, 10); - let xoffset = Random::int(5, 50); - let yoffset = Random::int(5, 10); - Container::new(Pos::new(width, height), Pos::new(xoffset, yoffset), true) -} diff --git a/src/user/lib/libgui_old_archive/libgui_elements.rs b/src/user/lib/libgui_old_archive/libgui_elements.rs deleted file mode 100644 index 44c4e09..0000000 --- a/src/user/lib/libgui_old_archive/libgui_elements.rs +++ /dev/null @@ -1,114 +0,0 @@ -use super::libgui_core::{self, Pos}; -use crate::std::io::println; -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; - -// TEXT BOX -// a widget to display text in a box -// has a title and a body -pub struct TextBox { - dimensions: Pos, - position: Pos, - content: String, - title: String, - outlined: bool, -} - -// implements all rendering for TextBox widget -impl libgui_core::Element for TextBox { - fn render(&self) -> (Vec>, Pos) { - let mut charmap = Vec::>::new(); - let mut inner_dims = Pos::new(self.dimensions.x - 2, self.dimensions.y - 2); - - // generate outline if required - if self.outlined { - charmap = libgui_core::gen_outline(self.dimensions); - } - - // render title - let mut titlechars = self.title.chars().collect::>(); - for (i, char) in titlechars.iter().enumerate() { - if i < inner_dims.x { - charmap[0][i + 1] = *char; - } else { - charmap[0][inner_dims.x - 0] = '.'; - charmap[0][inner_dims.x - 1] = '.'; - charmap[0][inner_dims.x - 2] = '.'; - break; - } - } - - // render text - let mut pos = Pos::new(0, 0); - - for chr in self.content.chars().collect::>() { - if pos.x < inner_dims.x { - if chr != '\n' { - charmap[pos.y + 1][pos.x + 1] = chr; - pos.x += 1; - } else { - pos.y += 1; - pos.x = 0; - } - } else { - // next line - pos.y += 1; - pos.x = 1; - - if pos.y < inner_dims.y { - charmap[pos.y + 1][1] = chr; - } else { - // handles overflow out of the end of the box - charmap[inner_dims.y][inner_dims.x] = '»'; - charmap[inner_dims.y + 1][inner_dims.x - 0] = '.'; - charmap[inner_dims.y + 1][inner_dims.x - 1] = '.'; - charmap[inner_dims.y + 1][inner_dims.x - 2] = '.'; - break; - } - } - } - - return (charmap, self.position); - } -} - -impl TextBox { - pub fn new( - title: String, - content: String, - dimensions: Pos, - position: Pos, - outlined: bool, - ) -> TextBox { - TextBox { - dimensions, - position, - content, - title, - outlined, - } - } -} - -struct IndicatorBox { - pub bars: Vec, - position: Pos, - dimensions: Pos, -} -impl IndicatorBox { - pub fn new(position: Pos, dimensions: Pos) -> IndicatorBox { - Self { - bars: Vec::new(), - position, - dimensions, - } - } - pub fn add_item(&mut self) {} -} -impl libgui_core::Element for IndicatorBox { - fn render(&self) -> (Vec>, Pos) { - (Vec::>::new(), Pos::new(0, 0)) - } -} diff --git a/src/user/lib/libgui_old_archive/mod.rs b/src/user/lib/libgui_old_archive/mod.rs deleted file mode 100644 index 6bbad83..0000000 --- a/src/user/lib/libgui_old_archive/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod libgui_core; -pub mod libgui_elements;