updates
updated grapher to use new widtgets system (not working yet but close) made a basic app using the widgets system implemented CgTextEdit for CgLineEdit (now fully working, but there may be bugs so i'll address them tomorrow)
This commit is contained in:
@@ -261,10 +261,16 @@ impl Renderer {
|
|||||||
if self.application_mode { return; }; // only in terminal mode
|
if self.application_mode { return; }; // only in terminal mode
|
||||||
self.term_buffer.push([ScreenChar::null(); BUFFER_WIDTH]);
|
self.term_buffer.push([ScreenChar::null(); BUFFER_WIDTH]);
|
||||||
self.col_pos = 0;
|
self.col_pos = 0;
|
||||||
|
if self.term_buffer.len() > 100 {
|
||||||
|
self.term_buffer.remove(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_lastline(&mut self) { // goes back to previous line and shifts all lines down
|
fn internal_lastline(&mut self) { // goes back to previous line and shifts all lines down
|
||||||
if self.application_mode { return; };
|
if self.application_mode { return; };
|
||||||
|
if self.term_buffer.len() <= 25 {
|
||||||
|
self.term_buffer.insert(0, [ScreenChar::null(); BUFFER_WIDTH]);
|
||||||
|
}
|
||||||
self.term_buffer.pop();
|
self.term_buffer.pop();
|
||||||
self.col_pos = BUFFER_WIDTH;
|
self.col_pos = BUFFER_WIDTH;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use x86_64::instructions::interrupts;
|
|||||||
|
|
||||||
use conquer_once::spin::OnceCell;
|
use conquer_once::spin::OnceCell;
|
||||||
use crossbeam_queue::ArrayQueue;
|
use crossbeam_queue::ArrayQueue;
|
||||||
use crate::println;
|
use crate::{println, serial_println};
|
||||||
|
|
||||||
use core::{pin::Pin, task::{Poll, Context}};
|
use core::{pin::Pin, task::{Poll, Context}};
|
||||||
use futures_util::stream::Stream;
|
use futures_util::stream::Stream;
|
||||||
@@ -15,8 +15,6 @@ use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1, Ke
|
|||||||
use crate::print;
|
use crate::print;
|
||||||
use crate::kernel::render::RENDERER;
|
use crate::kernel::render::RENDERER;
|
||||||
use alloc::{string::String};
|
use alloc::{string::String};
|
||||||
use core::ascii::Char;
|
|
||||||
use crate::kernel::tasks::keyboard::CharOrKeystroke::Char;
|
|
||||||
|
|
||||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
||||||
@@ -36,6 +34,7 @@ enum CharOrKeystroke {
|
|||||||
Keystroke(KeyCode),
|
Keystroke(KeyCode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum KeyStroke {
|
pub enum KeyStroke {
|
||||||
Char(char),
|
Char(char),
|
||||||
Ctrl,
|
Ctrl,
|
||||||
@@ -46,6 +45,9 @@ pub enum KeyStroke {
|
|||||||
RShift,
|
RShift,
|
||||||
Meta,
|
Meta,
|
||||||
RMeta,
|
RMeta,
|
||||||
|
Backspace,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,9 @@ impl KeyStroke {
|
|||||||
KeyCode::ShiftRight => KeyStroke::RShift,
|
KeyCode::ShiftRight => KeyStroke::RShift,
|
||||||
KeyCode::WindowsLeft => KeyStroke::Meta,
|
KeyCode::WindowsLeft => KeyStroke::Meta,
|
||||||
KeyCode::WindowsRight => KeyStroke::RMeta,
|
KeyCode::WindowsRight => KeyStroke::RMeta,
|
||||||
|
KeyCode::Backspace => KeyStroke::Backspace,
|
||||||
|
KeyCode::ArrowLeft => KeyStroke::Left,
|
||||||
|
KeyCode::ArrowRight => KeyStroke::Right,
|
||||||
_ => KeyStroke::None,
|
_ => KeyStroke::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,20 +86,12 @@ impl KeyboardHandler {
|
|||||||
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
|
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
|
||||||
if let Some(key) = self.keyboard.process_keyevent(key_event) {
|
if let Some(key) = self.keyboard.process_keyevent(key_event) {
|
||||||
match key {
|
match key {
|
||||||
DecodedKey::Unicode(character) => {
|
DecodedKey::Unicode(character) => return Some(KeyStroke::Char(character)),
|
||||||
if character == b'\x08' as char { // checks if the character is a backspace
|
|
||||||
interrupts::without_interrupts(|| {
|
|
||||||
RENDERER.lock().backspace(); // runs the backspace function of the vga buffer to remove the last character
|
|
||||||
});
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
return Some(KeyStroke::Char(character));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DecodedKey::RawKey(key) => {
|
DecodedKey::RawKey(key) => {
|
||||||
print!("{:?}", key)
|
print!("{:?}", key);
|
||||||
match key {
|
match KeyStroke::from_keycode(key) {
|
||||||
KeyCode::NOn
|
KeyStroke::None => (),
|
||||||
|
k => return Some(k)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -108,11 +105,8 @@ impl KeyboardHandler {
|
|||||||
loop {
|
loop {
|
||||||
match self.get_keystroke_inner().await {
|
match self.get_keystroke_inner().await {
|
||||||
Some(c) => match c {
|
Some(c) => match c {
|
||||||
CharOrKeystroke::Char(c) => return KeyStroke::Char(c),
|
KeyStroke::None => (),
|
||||||
CharOrKeystroke::Keystroke(c) => match KeyStroke::from_keycode(c) {
|
c => return c
|
||||||
KeyStroke::None => (),
|
|
||||||
key => return key
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => ()
|
None => ()
|
||||||
}
|
}
|
||||||
@@ -157,8 +151,16 @@ impl KeyboardHandler {
|
|||||||
None => { val.pop(); continue; },
|
None => { val.pop(); continue; },
|
||||||
};
|
};
|
||||||
|
|
||||||
if let CharOrKeystroke::Char(c) = character {
|
if let KeyStroke::Char(c) = character {
|
||||||
print!("{}", character);
|
if c == '\x08' {
|
||||||
|
val.pop();
|
||||||
|
interrupts::without_interrupts(|| {
|
||||||
|
RENDERER.lock().backspace(); // runs the backspace function of the vga buffer to remove the last character
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("{}", c);
|
||||||
let (c, execute): (char, bool) = match c {
|
let (c, execute): (char, bool) = match c {
|
||||||
'\n' => (c, true),
|
'\n' => (c, true),
|
||||||
_ => (c, false),
|
_ => (c, false),
|
||||||
|
|||||||
+10
-13
@@ -96,8 +96,7 @@ impl Frame {
|
|||||||
|
|
||||||
for line in elemstr.split("\n") {
|
for line in elemstr.split("\n") {
|
||||||
element.frame.push(
|
element.frame.push(
|
||||||
line
|
line.chars()
|
||||||
.chars()
|
|
||||||
.map(|c| ColouredChar::new(c))
|
.map(|c| ColouredChar::new(c))
|
||||||
.collect::<Vec<ColouredChar>>()
|
.collect::<Vec<ColouredChar>>()
|
||||||
);
|
);
|
||||||
@@ -105,7 +104,7 @@ impl Frame {
|
|||||||
|
|
||||||
for row in element.clone().frame {
|
for row in element.clone().frame {
|
||||||
let n = row.len();
|
let n = row.len();
|
||||||
if n > element.dimensions.x as usize {
|
if n > element.dimensions.x {
|
||||||
element.dimensions.x = n;
|
element.dimensions.x = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,21 +115,19 @@ impl Frame {
|
|||||||
self.frame.clone()
|
self.frame.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_to_screen(&self) -> Result<(), RenderError> {
|
pub fn write_to_screen(&self) -> Result<(), RenderError> {
|
||||||
let mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
let mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
||||||
for (i, row) in self.frame.iter().enumerate() {
|
for (i, row) in self.frame.iter().enumerate() {
|
||||||
for (j, col) in row.iter().enumerate() {
|
for (j, col) in row.iter().enumerate() {
|
||||||
//println!("{} {} {}", i, j, col);
|
frame[i + self.position.y][j + self.position.x] = col.as_screen_char();
|
||||||
frame[i + self.position.y as usize][j + self.position.x as usize] = col.as_screen_char();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
RENDERER.lock().render_frame(frame);
|
RENDERER.lock().render_frame(frame);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn position(&self) -> Position {
|
pub fn get_position(&self) -> Position {
|
||||||
self.position
|
self.position
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(&mut self, position: Position) {
|
pub fn set_position(&mut self, position: Position) {
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
@@ -140,7 +137,7 @@ impl Frame {
|
|||||||
pub fn write(&mut self, position: Position, char: ColouredChar) {
|
pub fn write(&mut self, position: Position, char: ColouredChar) {
|
||||||
self.frame[position.y][position.x] = char
|
self.frame[position.y][position.x] = char
|
||||||
}
|
}
|
||||||
pub fn render_element(&mut self, other: &Frame) {
|
pub fn place_child_element(&mut self, other: &Frame) {
|
||||||
for (i, row) in other.frame.iter().enumerate() {
|
for (i, row) in other.frame.iter().enumerate() {
|
||||||
for (j, chr) in row.iter().enumerate() {
|
for (j, chr) in row.iter().enumerate() {
|
||||||
self.frame[i + other.position.y][j + other.position.x] = *chr
|
self.frame[i + other.position.y][j + other.position.x] = *chr
|
||||||
@@ -152,20 +149,20 @@ impl Frame {
|
|||||||
|
|
||||||
let (mut x, mut y) = (false, false);
|
let (mut x, mut y) = (false, false);
|
||||||
|
|
||||||
if element.dimensions().x + element.position().x > self.dimensions.x {
|
if element.dimensions().x + element.get_position().x > self.dimensions.x {
|
||||||
if should_panic { panic!(
|
if should_panic { panic!(
|
||||||
"Element is to large to be rendered {} {}",
|
"Element is to large to be rendered {} {}",
|
||||||
element.dimensions().x + element.position().x,
|
element.dimensions().x + element.get_position().x,
|
||||||
self.dimensions.x
|
self.dimensions.x
|
||||||
)} else {
|
)} else {
|
||||||
x = true;
|
x = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.dimensions().y + element.position().y > self.dimensions.y {
|
if element.dimensions().y + element.get_position().y > self.dimensions.y {
|
||||||
if should_panic { panic!(
|
if should_panic { panic!(
|
||||||
"Element is to large to be rendered {} {}",
|
"Element is to large to be rendered {} {}",
|
||||||
element.dimensions().y + element.position().y,
|
element.dimensions().y + element.get_position().y,
|
||||||
self.dimensions.y
|
self.dimensions.y
|
||||||
)} else {
|
)} else {
|
||||||
y = true;
|
y = true;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
kernel::render::{RENDERER, self},
|
kernel::render::{RENDERER, self},
|
||||||
kernel::tasks::keyboard::{KEYBOARD, KeyStroke},
|
kernel::tasks::keyboard::{KEYBOARD},
|
||||||
};
|
};
|
||||||
|
pub use crate::kernel::tasks::keyboard::KeyStroke;
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -17,6 +18,7 @@ use crate::std::frame::RenderError;
|
|||||||
|
|
||||||
pub struct Stdin {}
|
pub struct Stdin {}
|
||||||
impl Stdin {
|
impl Stdin {
|
||||||
|
pub const BACKSPACE: char = b'\x08' as char;
|
||||||
/// waits for the user to type in a string and press enter | blocking
|
/// waits for the user to type in a string and press enter | blocking
|
||||||
pub async fn readline() -> String {
|
pub async fn readline() -> String {
|
||||||
let string = KEYBOARD.lock().get_string().await;
|
let string = KEYBOARD.lock().get_string().await;
|
||||||
@@ -24,13 +26,13 @@ impl Stdin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// waits for a keystroke | blocking
|
/// waits for a keystroke | blocking
|
||||||
pub async fn keystroke() -> char {
|
pub async fn keystroke() -> KeyStroke {
|
||||||
let chr = KEYBOARD.lock().get_keystroke().await;
|
let chr = KEYBOARD.lock().get_keystroke().await;
|
||||||
chr
|
chr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// gets the next keystroke if any is present | non blocking
|
/// gets the next keystroke if any is present | non blocking
|
||||||
pub fn try_keystroke() -> Option<char> {
|
pub fn try_keystroke() -> Option<KeyStroke> {
|
||||||
let chr = KEYBOARD.lock().try_keystroke();
|
let chr = KEYBOARD.lock().try_keystroke();
|
||||||
chr
|
chr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use crate::std::io::{KeyStroke, Stdin};
|
||||||
|
|
||||||
pub struct GameLoop;
|
pub struct GameLoop;
|
||||||
|
|
||||||
@@ -98,7 +99,9 @@ impl Application for GameLoop {
|
|||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("{}", io::Stdin::keystroke().await)
|
if let KeyStroke::Char(c) = Stdin::keystroke().await {
|
||||||
|
println!("{}", c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use async_trait::async_trait;
|
|||||||
use crate::kernel::render::{Color, ColorCode};
|
use crate::kernel::render::{Color, ColorCode};
|
||||||
use crate::{println, serial_println};
|
use crate::{println, serial_println};
|
||||||
use crate::std::frame::{ColouredChar, Frame, Position, Dimensions, RenderError};
|
use crate::std::frame::{ColouredChar, Frame, Position, Dimensions, RenderError};
|
||||||
use crate::std::io::{Screen, Stdin};
|
use crate::std::io::{KeyStroke, Screen, Stdin};
|
||||||
use crate::std::time::wait;
|
use crate::std::time::wait;
|
||||||
use crate::user::bin::snake::Game;
|
use crate::user::bin::snake::Game;
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ impl GameOfLife {
|
|||||||
|
|
||||||
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||||
match Stdin::try_keystroke() {
|
match Stdin::try_keystroke() {
|
||||||
Some('x') => break 'mainloop,
|
Some(KeyStroke::Char('x')) => break 'mainloop,
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ impl GameOfLife {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self) -> Result<(), RenderError> {
|
fn render(&self) -> Result<(), RenderError> {
|
||||||
self.frame.render_to_screen()?;
|
self.frame.write_to_screen()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+105
-30
@@ -4,13 +4,18 @@ use alloc::vec::Vec;
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::{println, serial_println};
|
use crate::{println, serial_println};
|
||||||
|
use crate::kernel::render::RenderError;
|
||||||
use crate::shell::command_handler;
|
use crate::shell::command_handler;
|
||||||
use crate::std::application::{Application, Error};
|
use crate::std::application::{Application, Error};
|
||||||
use crate::std::frame::{self, Frame, Position, Dimensions, ColouredChar};
|
use crate::std::frame::{self, Frame, Position, Dimensions, ColouredChar};
|
||||||
use crate::std::io::{Screen, Stdin};
|
use crate::std::io::{KeyStroke, Screen, Stdin};
|
||||||
|
use crate::user::lib::libgui::cg_core::{CgComponent, CgTextEdit};
|
||||||
|
use crate::user::lib::libgui::cg_inputs::CgLineEdit;
|
||||||
|
use crate::user::lib::libgui::cg_widgets::CgContainer;
|
||||||
|
use super::calc;
|
||||||
|
|
||||||
const OFFSET_X: i64 = 40;
|
const OFFSET_X: i64 = 39;
|
||||||
const OFFSET_Y: i64 = 12;
|
const OFFSET_Y: i64 = 10;
|
||||||
|
|
||||||
pub struct Grapher {
|
pub struct Grapher {
|
||||||
points: Vec<PointF64>,
|
points: Vec<PointF64>,
|
||||||
@@ -32,15 +37,86 @@ impl Application for Grapher {
|
|||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
points: Vec::new(),
|
points: Vec::new(),
|
||||||
frame: Frame::new(Position::new(0, 0), Dimensions::new(80, 25)).unwrap()
|
frame: Frame::new(Position::new(0, 0), Dimensions::new(78, 22)).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||||
let mut equation: String = args.into_iter().collect();
|
Screen::Application.set_mode().map_err(|_| Error::ApplicationError(String::from("failed to set application mode")))?;
|
||||||
use super::calc;
|
|
||||||
|
if args.len() > 0 {
|
||||||
|
let equation: String = args.into_iter().collect();
|
||||||
|
self.graph_equation(equation);
|
||||||
|
|
||||||
|
if let Ok(frame) = self.render() {
|
||||||
|
frame.write_to_screen().map_err(|_| Error::ApplicationError(String::from("failed to write to screen")))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match Stdin::keystroke().await {
|
||||||
|
KeyStroke::Char('x') => break,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Screen::Terminal.set_mode().map_err(|_| Error::ApplicationError(String::from("failed to set terminal mode")))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let mut entry_box = CgLineEdit::new(
|
||||||
|
Position::new(1, 23),
|
||||||
|
78,
|
||||||
|
String::from("function >")
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut commandresult = String::new();
|
||||||
|
|
||||||
|
while let c = Stdin::keystroke().await {
|
||||||
|
let mut container = CgContainer::new(
|
||||||
|
Position::new(0, 0),
|
||||||
|
Dimensions::new(80, 25),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
match c {
|
||||||
|
KeyStroke::Char('\n') => {
|
||||||
|
commandresult = entry_box.text.iter().collect();
|
||||||
|
entry_box.clear();
|
||||||
|
},
|
||||||
|
KeyStroke::Char(Stdin::BACKSPACE) => {
|
||||||
|
serial_println!("backspace");
|
||||||
|
entry_box.backspace()
|
||||||
|
},
|
||||||
|
KeyStroke::Char(c) => entry_box.write_char(c),
|
||||||
|
KeyStroke::Left => entry_box.move_cursor(false),
|
||||||
|
KeyStroke::Right => entry_box.move_cursor(true),
|
||||||
|
KeyStroke::Alt => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandresult.len() > 0 {
|
||||||
|
let equation = commandresult.chars().take(40).collect();
|
||||||
|
self.graph_equation(equation);
|
||||||
|
commandresult.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
container.insert(Box::new(self));
|
||||||
|
container.insert(Box::new(&entry_box));
|
||||||
|
|
||||||
|
if let Ok(frame) = container.render() {
|
||||||
|
frame.write_to_screen().map_err(|_| Error::ApplicationError(String::from("failed to write to screen")))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grapher {
|
||||||
|
|
||||||
|
fn graph_equation(&mut self, equation: String) {
|
||||||
|
|
||||||
let cal = calc::Calculator::new();
|
let cal = calc::Calculator::new();
|
||||||
|
|
||||||
for x in -4000..4000 {
|
for x in -4000..4000 {
|
||||||
let x = x as f64 / 100.0;
|
let x = x as f64 / 100.0;
|
||||||
|
|
||||||
@@ -48,8 +124,7 @@ impl Application for Grapher {
|
|||||||
if c == 'x' { format!("({})", x) } else { c.to_string() }
|
if c == 'x' { format!("({})", x) } else { c.to_string() }
|
||||||
}).collect::<String>();
|
}).collect::<String>();
|
||||||
|
|
||||||
let fx = cal.calculate(new_eq).map_err(|_| Error::ApplicationError(String::from("failed to calculate")));
|
let fx = cal.calculate(new_eq);
|
||||||
|
|
||||||
if let Ok(y) = fx {
|
if let Ok(y) = fx {
|
||||||
self.render_point(PointF64 {
|
self.render_point(PointF64 {
|
||||||
x,
|
x,
|
||||||
@@ -57,45 +132,45 @@ impl Application for Grapher {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Screen::Application.set_mode().map_err(|_| Error::ApplicationError(String::from("failed to set application mode")))?;
|
|
||||||
self.display();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match Stdin::keystroke().await {
|
|
||||||
'x' => break,
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Screen::Terminal.set_mode().map_err(|_| Error::ApplicationError(String::from("failed to set terminal mode")))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Grapher {
|
|
||||||
|
|
||||||
fn render_point(&mut self, point: PointF64) {
|
fn render_point(&mut self, point: PointF64) {
|
||||||
|
|
||||||
let point = PointI64 {
|
let point = PointI64 {
|
||||||
x: point.x as i64,
|
x: point.x as i64,
|
||||||
y: point.y as i64,
|
y: point.y as i64,
|
||||||
};
|
};
|
||||||
|
|
||||||
if point.x < -40 || point.x >= 40 || point.y < -12 || point.y >= 12 {
|
if point.x < -39 || point.x >= 39 || point.y < -10 || point.y >= 12 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset_x = point.x + OFFSET_X;
|
let offset_x = point.x + OFFSET_X;
|
||||||
let offset_y = point.y + OFFSET_Y;
|
let offset_y = point.y + OFFSET_Y;
|
||||||
|
|
||||||
// serial_println!("{} {}", 24-offset_y as usize, offset_x as usize);
|
self.frame.write(Position::new(offset_x as usize, 22-offset_y as usize), ColouredChar::new('*'));
|
||||||
|
|
||||||
self.frame.write(Position::new(offset_x as usize, 24-offset_y as usize), ColouredChar::new('*'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn display(&mut self) {
|
fn display(&mut self) {
|
||||||
self.frame.render_to_screen().unwrap();
|
self.frame.write_to_screen().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CgComponent for Grapher {
|
||||||
|
fn render(&self) -> Result<Frame, RenderError> {
|
||||||
|
Ok(self.frame.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+63
-24
@@ -9,7 +9,7 @@ use vga::writers::{GraphicsWriter, PrimitiveDrawing};
|
|||||||
use crate::{print, printerr, println, serial_println, std, std::application::{Application, Error}, user::bin::*};
|
use crate::{print, printerr, println, serial_println, std, std::application::{Application, Error}, user::bin::*};
|
||||||
use crate::kernel::render::ColorCode;
|
use crate::kernel::render::ColorCode;
|
||||||
use crate::std::frame::{Dimensions, Position};
|
use crate::std::frame::{Dimensions, Position};
|
||||||
use crate::std::io::{Color, write, Screen, Stdin, Serial};
|
use crate::std::io::{Color, write, Screen, Stdin, Serial, KeyStroke};
|
||||||
use crate::std::random::Random;
|
use crate::std::random::Random;
|
||||||
use crate::user::bin::gigachad_detector::GigachadDetector;
|
use crate::user::bin::gigachad_detector::GigachadDetector;
|
||||||
use crate::user::bin::grapher::Grapher;
|
use crate::user::bin::grapher::Grapher;
|
||||||
@@ -17,6 +17,7 @@ use crate::user::lib::libgui::{
|
|||||||
cg_core::{CgComponent},
|
cg_core::{CgComponent},
|
||||||
cg_widgets::{CgTextBox, CgContainer},
|
cg_widgets::{CgTextBox, CgContainer},
|
||||||
};
|
};
|
||||||
|
use crate::user::lib::libgui::cg_core::CgTextEdit;
|
||||||
use crate::user::lib::libgui::cg_inputs::CgLineEdit;
|
use crate::user::lib::libgui::cg_inputs::CgLineEdit;
|
||||||
use crate::user::lib::libgui::cg_widgets::{CgIndicatorBar, CgIndicatorWidget, CgLabel, CgStatusBar};
|
use crate::user::lib::libgui::cg_widgets::{CgIndicatorBar, CgIndicatorWidget, CgLabel, CgStatusBar};
|
||||||
|
|
||||||
@@ -190,8 +191,8 @@ async fn exec() -> Result<(), Error> {
|
|||||||
"test_features" => {
|
"test_features" => {
|
||||||
Screen::Application.set_mode().unwrap();
|
Screen::Application.set_mode().unwrap();
|
||||||
|
|
||||||
new_eventloop(|c| match c {
|
setup_ui(|c| match c {
|
||||||
'x' => ('x', true),
|
KeyStroke::Char('x') => (c, true),
|
||||||
_ => (c, false),
|
_ => (c, false),
|
||||||
}).await;
|
}).await;
|
||||||
|
|
||||||
@@ -259,7 +260,7 @@ struct CmdHistory {
|
|||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn new_eventloop(input: impl Fn(char) -> (char, bool)) {
|
async fn setup_ui(input: impl Fn(KeyStroke) -> (KeyStroke, bool)) {
|
||||||
|
|
||||||
let textbox = CgTextBox::new(
|
let textbox = CgTextBox::new(
|
||||||
String::from("i'd just like to interject for a moment"),
|
String::from("i'd just like to interject for a moment"),
|
||||||
@@ -269,42 +270,80 @@ async fn new_eventloop(input: impl Fn(char) -> (char, bool)) {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut label = CgLabel::new(
|
||||||
|
String::from("test label"),
|
||||||
|
Position::new(1, 1),
|
||||||
|
40,
|
||||||
|
);
|
||||||
|
|
||||||
let mut statusbar = CgStatusBar::new(Position::new(0, 0), Dimensions::new(80, 1));
|
let mut statusbar = CgStatusBar::new(Position::new(0, 0), Dimensions::new(80, 1));
|
||||||
|
|
||||||
let mut textedit = CgLineEdit::new(
|
let mut textedit = CgLineEdit::new(
|
||||||
Position::new(0, 1),
|
Position::new(0, 20),
|
||||||
80,
|
80,
|
||||||
String::from("enter text here >"),
|
String::from("enter text here >"),
|
||||||
);
|
);
|
||||||
textedit.text = "hello".chars().collect();
|
|
||||||
|
|
||||||
let mut container = CgContainer::new(
|
let mut commandresult = String::new();
|
||||||
Position::new(0, 0),
|
|
||||||
Dimensions::new(80, 25),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
container.elements.push(Box::new(&textbox));
|
|
||||||
container.elements.push(Box::new(&statusbar));
|
|
||||||
container.elements.push(Box::new(&textedit));
|
|
||||||
|
|
||||||
while let (c, false) = input(Stdin::keystroke().await) {
|
while let (c, false) = input(Stdin::keystroke().await) {
|
||||||
|
|
||||||
|
|
||||||
textedit.text.push(c);
|
|
||||||
|
|
||||||
let mut container = CgContainer::new(
|
let mut container = CgContainer::new(
|
||||||
Position::new(0, 0),
|
Position::new(0, 0),
|
||||||
Dimensions::new(80, 25),
|
Dimensions::new(80, 25),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
container.elements.push(Box::new(&textbox));
|
match c {
|
||||||
container.elements.push(Box::new(&statusbar));
|
KeyStroke::Char('\n') => {
|
||||||
container.elements.push(Box::new(&textedit));
|
commandresult = textedit.text.iter().collect();
|
||||||
|
textedit.clear();
|
||||||
|
},
|
||||||
|
KeyStroke::Char(Stdin::BACKSPACE) => {
|
||||||
|
serial_println!("backspace");
|
||||||
|
textedit.backspace()
|
||||||
|
},
|
||||||
|
KeyStroke::Char(c) => textedit.write_char(c),
|
||||||
|
KeyStroke::Left => textedit.move_cursor(false),
|
||||||
|
KeyStroke::Right => textedit.move_cursor(true),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if commandresult.len() > 0 {
|
||||||
|
let string = commandresult.chars().take(40).collect();
|
||||||
|
label.set_text(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.insert(Box::new(&textbox));
|
||||||
|
container.insert(Box::new(&statusbar));
|
||||||
|
container.insert(Box::new(&textedit));
|
||||||
|
container.insert(Box::new(&label));
|
||||||
|
|
||||||
if let Ok(frame) = container.render() {
|
if let Ok(frame) = container.render() {
|
||||||
frame.render_to_screen().unwrap();
|
frame.write_to_screen().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use alloc::borrow::ToOwned;
|
|||||||
use core::arch::x86_64::_mm_test_all_ones;
|
use core::arch::x86_64::_mm_test_all_ones;
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crate::std::io::{Color, Screen, Stdin};
|
use crate::std::io::{Color, KeyStroke, Screen, Stdin};
|
||||||
use crate::std::time;
|
use crate::std::time;
|
||||||
use crate::kernel::tasks::keyboard::KEYBOARD;
|
use crate::kernel::tasks::keyboard::KEYBOARD;
|
||||||
use crossbeam_queue::SegQueue;
|
use crossbeam_queue::SegQueue;
|
||||||
@@ -138,7 +138,7 @@ impl Game {
|
|||||||
// loop triggers when game is lost
|
// loop triggers when game is lost
|
||||||
loop {
|
loop {
|
||||||
match Stdin::keystroke().await {
|
match Stdin::keystroke().await {
|
||||||
'x' => break 'gameloop,
|
KeyStroke::Char('x') => break 'gameloop,
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ impl Game {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frame.render_to_screen()?;
|
frame.write_to_screen()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ impl Game {
|
|||||||
frame[12] = Game::centre_text(80, String::from(format!("ur score was {}", self.score))).chars().map(|c| ColouredChar::coloured(c, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
frame[12] = Game::centre_text(80, String::from(format!("ur score was {}", self.score))).chars().map(|c| ColouredChar::coloured(c, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
||||||
frame[14] = Game::centre_text(80, String::from("L bozo")).chars().map(|c| ColouredChar::coloured(c, ColorCode::new(Color::Red, Color::Black))).collect();
|
frame[14] = Game::centre_text(80, String::from("L bozo")).chars().map(|c| ColouredChar::coloured(c, ColorCode::new(Color::Red, Color::Black))).collect();
|
||||||
|
|
||||||
frame.render_to_screen()?;
|
frame.write_to_screen()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,7 +272,7 @@ impl Snake {
|
|||||||
if self.ai_controlled {
|
if self.ai_controlled {
|
||||||
self.dir = PathFinder::decide(&self.head, tails, points_of_interest);
|
self.dir = PathFinder::decide(&self.head, tails, points_of_interest);
|
||||||
} else {
|
} else {
|
||||||
if let Some(c) = Stdin::try_keystroke() {
|
if let Some(KeyStroke::Char(c)) = Stdin::try_keystroke() {
|
||||||
self.dir = match c {
|
self.dir = match c {
|
||||||
'w' => Direction::Degrees0,
|
'w' => Direction::Degrees0,
|
||||||
'a' => Direction::Degrees270,
|
'a' => Direction::Degrees270,
|
||||||
|
|||||||
@@ -1,290 +0,0 @@
|
|||||||
use alloc::string::String;
|
|
||||||
use alloc::{format, vec, vec::Vec, boxed::Box};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use crate::std::io::{Color, Screen, Stdin};
|
|
||||||
use crate::std::time;
|
|
||||||
use crate::kernel::tasks::keyboard::KEYBOARD;
|
|
||||||
use crossbeam_queue::SegQueue;
|
|
||||||
use crate::kernel::render::{ColorCode, ScreenChar};
|
|
||||||
use crate::std::application::{Application, Error};
|
|
||||||
use crate::std::random::Random;
|
|
||||||
use crate::system::std::frame::ColouredElement;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct Point {
|
|
||||||
x: i8,
|
|
||||||
y: i8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
struct Position {
|
|
||||||
x: i8,
|
|
||||||
y: i8,
|
|
||||||
dir: Direction,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
enum Direction {
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
Scored,
|
|
||||||
Lost,
|
|
||||||
Exited,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Game {
|
|
||||||
snake: SegQueue<Point>,
|
|
||||||
head: Point,
|
|
||||||
poi: Point,
|
|
||||||
mv: char,
|
|
||||||
score: u8,
|
|
||||||
hardmode: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
snake.rs
|
|
||||||
#[async_trait]
|
|
||||||
impl Application for Game {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
snake: SegQueue::new(),
|
|
||||||
head: Point { x: 5, y: 5 },
|
|
||||||
poi: Point { x: 0, y: 0 },
|
|
||||||
mv: ' ',
|
|
||||||
score: 0,
|
|
||||||
hardmode: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
|
|
||||||
Screen::application_mode();
|
|
||||||
let clone = self.clone_snake();
|
|
||||||
|
|
||||||
// render the initial state of the screen.
|
|
||||||
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
|
||||||
|
|
||||||
|
|
||||||
(5..=7).for_each(|x| {
|
|
||||||
self.snake.push(Point { x, y: 5 });
|
|
||||||
});
|
|
||||||
self.head = Point { x: 7, y: 5 };
|
|
||||||
self.new_poi();
|
|
||||||
|
|
||||||
self.gameloop().await?;
|
|
||||||
|
|
||||||
Screen::terminal_mode();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Game {
|
|
||||||
|
|
||||||
async fn gameloop(&mut self) -> Result<(), Error> { // main gameloop
|
|
||||||
'gameloop: loop {
|
|
||||||
|
|
||||||
time::wait(0.1);
|
|
||||||
|
|
||||||
if let Some(c) = Stdin::try_keystroke() {
|
|
||||||
self.mv = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
//self.mv = Stdin::keystroke().await;
|
|
||||||
|
|
||||||
match self.mv {
|
|
||||||
'w' => self.head.y -= 1,
|
|
||||||
'a' => self.head.x -= 1,
|
|
||||||
's' => self.head.y += 1,
|
|
||||||
'd' => self.head.x += 1,
|
|
||||||
'x' => break,
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.snake.push(Point { x: self.head.x, y: self.head.y }); // new head added
|
|
||||||
|
|
||||||
if self.head == self.poi {
|
|
||||||
self.new_poi();
|
|
||||||
self.score += 1
|
|
||||||
} else {
|
|
||||||
self.snake.pop().unwrap(); // tail removed if score does not increase
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.lose_condition() {
|
|
||||||
self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render game over screen")))?;
|
|
||||||
while let chr = KEYBOARD.lock().get_keystroke().await {
|
|
||||||
match chr {
|
|
||||||
'x' => break 'gameloop,
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let clone = self.clone_snake();
|
|
||||||
self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_poi(&mut self) {
|
|
||||||
self.poi = Point { x: Random::int(3, 76) as i8, y: Random::int(3, 21) as i8 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, snake: Vec<Point>) -> Result<(), ()> {
|
|
||||||
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
|
||||||
snake.into_iter().for_each(|p| {
|
|
||||||
frame[p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black));
|
|
||||||
});
|
|
||||||
|
|
||||||
frame[self.poi.y as usize][self.poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black));
|
|
||||||
let literal = format!("snake go brr score: {}", self.score);
|
|
||||||
let msg = Game::centre_text(80, literal);
|
|
||||||
frame[1] = msg.chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
|
||||||
|
|
||||||
let mut elem = ColouredElement::generate(frame, (80, 25));
|
|
||||||
elem.render((0,0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lose_condition(&mut self) -> bool {
|
|
||||||
let cloned = self.clone_snake();
|
|
||||||
let snake_overlaps = (1..cloned.len()).any(|i| cloned[i..].contains(&cloned[i - 1])); // checks if any part of the snake overlaps itself
|
|
||||||
let out_of_bounds = cloned.iter().filter(|p| p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24).count() > 0; // checks if the snake goes out of bounds
|
|
||||||
|
|
||||||
snake_overlaps || out_of_bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
fn centre_text(dims: usize, text: String) -> String { // centres text in a string of whitespace of a given length
|
|
||||||
let max_pad = dims / 2;
|
|
||||||
let mut msg = String::new();
|
|
||||||
msg.push_str(" ".repeat(max_pad - round_up(text.len() as f64 / 2.0)).as_str());
|
|
||||||
msg.push_str(text.as_str());
|
|
||||||
msg.push_str(" ".repeat(max_pad - round_down(text.len() as f64 / 2.0 + 0.51)).as_str());
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_end_screen(&mut self) -> Result<(), ()> {
|
|
||||||
let mut frame = vec![vec![ScreenChar::null(); 80]; 25];
|
|
||||||
|
|
||||||
frame[10] = Game::centre_text(80, String::from("u lost")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect();
|
|
||||||
frame[12] = Game::centre_text(80, String::from(format!("ur score was {}", self.score))).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect();
|
|
||||||
frame[14] = Game::centre_text(80, String::from("L bozo")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect();
|
|
||||||
|
|
||||||
|
|
||||||
let mut elem = ColouredElement::generate(frame, (80, 25));
|
|
||||||
elem.render((0,0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_snake(&mut self) -> Vec<Point> {
|
|
||||||
let mut cloned= Vec::new();
|
|
||||||
let mut snake = SegQueue::new();
|
|
||||||
while !self.snake.is_empty() {
|
|
||||||
let item = self.snake.pop().unwrap();
|
|
||||||
cloned.push(item.clone());
|
|
||||||
snake.push(item);
|
|
||||||
}
|
|
||||||
self.snake = snake;
|
|
||||||
cloned
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Snake {
|
|
||||||
ai_controlled: bool,
|
|
||||||
head: Point,
|
|
||||||
tail: Vec<Point>,
|
|
||||||
dir: Direction,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Snake {
|
|
||||||
fn ai(id: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
ai_controlled: true,
|
|
||||||
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
|
||||||
tail: Vec::new(),
|
|
||||||
dir: Direction::Up
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn player(id: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
ai_controlled: false,
|
|
||||||
head: Point { x: 1 + id as i8 * 2, y: 1 },
|
|
||||||
tail: Vec::new(),
|
|
||||||
dir: Direction::Up,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&mut self, points_of_interest: &Vec<Point>, tails: &Vec<Point>) -> Status { // returns (lose_condition, scored)
|
|
||||||
|
|
||||||
// uses pathing algorithm if ai else keyboard input if human
|
|
||||||
if self.ai_controlled {
|
|
||||||
self.dir = self.decide_dir();
|
|
||||||
} else {
|
|
||||||
if let Some(c) = Stdin::try_keystroke() {
|
|
||||||
self.dir = match c {
|
|
||||||
'w' => Direction::Up,
|
|
||||||
'a' => Direction::Left,
|
|
||||||
's' => Direction::Down,
|
|
||||||
'd' => Direction::Right,
|
|
||||||
'x' => return Status::Exited,
|
|
||||||
_ => self.dir.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tail.push(self.head.clone());
|
|
||||||
|
|
||||||
match self.dir {
|
|
||||||
Direction::Up => self.head.y -= 1,
|
|
||||||
Direction::Down => self.head.y += 1,
|
|
||||||
Direction::Left => self.head.x -= 1,
|
|
||||||
Direction::Right => self.head.x += 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.lose_condition(tails) {
|
|
||||||
return Status::Lost;
|
|
||||||
}
|
|
||||||
|
|
||||||
if points_of_interest.contains(&self.head) {
|
|
||||||
Status::Scored
|
|
||||||
} else {
|
|
||||||
self.tail.remove(0);
|
|
||||||
Status::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decide_dir(&mut self) -> Direction {
|
|
||||||
unimplemented!() // implement a basic pathfinding or random movement algorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lose_condition(&mut self, tails: &Vec<Point>) -> bool { // where tails includes the tail of every other snake
|
|
||||||
let p = self.head.clone();
|
|
||||||
|
|
||||||
let snake_overlaps = tails.contains(&self.head); // checks if any part of the snake overlaps itself
|
|
||||||
let out_of_bounds = p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24; // checks if the snake goes out of bounds
|
|
||||||
|
|
||||||
snake_overlaps || out_of_bounds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn round_up(n: f64) -> usize {
|
|
||||||
(n + 0.99) as usize
|
|
||||||
}
|
|
||||||
fn round_down(n: f64) -> usize {
|
|
||||||
n as usize
|
|
||||||
}
|
|
||||||
@@ -18,12 +18,12 @@ pub trait CgComponent {
|
|||||||
fn render(&self) -> Result<Frame, RenderError>;
|
fn render(&self) -> Result<Frame, RenderError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// trait for components that can have editable text, such as search boxes, command palletes, terminals, text inputs etc.
|
/// trait for components that can have editable text, such as search boxes, command palettes, terminals, text inputs etc.
|
||||||
pub trait CgTextEdit: CgComponent {
|
pub trait CgTextEdit: CgComponent {
|
||||||
fn write_char(&self) -> Result<Frame, RenderError>; // this can also be implemented in a way that inserts characters
|
fn write_char(&mut self, c: char); // this can also be implemented in a way that inserts characters
|
||||||
fn delete_char(&self) -> Result<Frame, RenderError>;
|
fn backspace(&mut self);
|
||||||
fn move_cursor(&self, direction: bool) -> Result<(), RenderError>; // true = right, false = left
|
fn move_cursor(&mut self, direction: bool); // true = right, false = left
|
||||||
fn clear(&self) -> Result<Frame, RenderError>;
|
fn clear(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use alloc::vec::Vec;
|
|||||||
use crate::std::frame::{ColouredChar, Dimensions, Frame, Position, RenderError};
|
use crate::std::frame::{ColouredChar, Dimensions, Frame, Position, RenderError};
|
||||||
use crate::user::lib::libgui::cg_core::{CgComponent, CgTextEdit};
|
use crate::user::lib::libgui::cg_core::{CgComponent, CgTextEdit};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgLineEdit {
|
pub struct CgLineEdit {
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
pub dimensions: Dimensions,
|
pub dimensions: Dimensions,
|
||||||
@@ -26,28 +27,56 @@ impl CgLineEdit {
|
|||||||
impl CgComponent for CgLineEdit {
|
impl CgComponent for CgLineEdit {
|
||||||
fn render(&self) -> Result<Frame, RenderError> {
|
fn render(&self) -> Result<Frame, RenderError> {
|
||||||
let mut frame = Frame::new(self.position, self.dimensions)?;
|
let mut frame = Frame::new(self.position, self.dimensions)?;
|
||||||
|
let mut idx = 0;
|
||||||
let mut ptr = 0;
|
|
||||||
|
|
||||||
for c in self.prompt.chars() {
|
for c in self.prompt.chars() {
|
||||||
if ptr >= self.dimensions.x {
|
if idx >= self.dimensions.x {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
frame.write(Position::new(ptr, 0), ColouredChar::new(c));
|
frame.write(Position::new(idx, 0), ColouredChar::new(c));
|
||||||
ptr += 1
|
idx += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr += 1; // create a space between the prompt and the text
|
idx += 1; // create a space between the prompt and the text
|
||||||
|
|
||||||
for c in self.text.iter() {
|
if idx + self.text.len() > self.dimensions.x {
|
||||||
if ptr >= self.dimensions.x {
|
frame.write(Position::new(idx, 0), ColouredChar::new('['));
|
||||||
break;
|
frame.write(Position::new(idx + 1, 0), ColouredChar::new('.'));
|
||||||
}
|
frame.write(Position::new(idx + 2, 0), ColouredChar::new('.'));
|
||||||
frame.write(Position::new(ptr, 0), ColouredChar::new(*c));
|
frame.write(Position::new(idx + 3, 0), ColouredChar::new('.'));
|
||||||
ptr += 1
|
frame.write(Position::new(idx + 4, 0), ColouredChar::new(']'));
|
||||||
|
idx += 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
self.text.iter().rev().take(self.dimensions.x - idx).rev().for_each(|c| {
|
||||||
|
frame.write(Position::new(idx, 0), ColouredChar::new(*c));
|
||||||
|
idx += 1
|
||||||
|
});
|
||||||
|
|
||||||
Ok(frame)
|
Ok(frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CgTextEdit for CgLineEdit {
|
||||||
|
fn write_char(&mut self, c: char) {
|
||||||
|
self.text.insert(self.ptr, c);
|
||||||
|
self.ptr += 1;
|
||||||
|
}
|
||||||
|
fn backspace(&mut self) {
|
||||||
|
if self.ptr >= 0 {
|
||||||
|
self.ptr -= 1;
|
||||||
|
self.text.remove(self.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn move_cursor(&mut self, direction: bool) {
|
||||||
|
match direction {
|
||||||
|
true => if self.ptr < self.text.len() { self.ptr += 1; },
|
||||||
|
false => if self.ptr > 0 { self.ptr -= 1; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.text.clear();
|
||||||
|
self.ptr = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use super::cg_core::{
|
|||||||
use crate::std::frame::{ColouredChar, Dimensions, Position, Frame};
|
use crate::std::frame::{ColouredChar, Dimensions, Position, Frame};
|
||||||
use crate::std::io::Color;
|
use crate::std::io::Color;
|
||||||
|
|
||||||
|
|
||||||
pub struct CgContainer<'a> {
|
pub struct CgContainer<'a> {
|
||||||
pub elements: Vec<Box<&'a dyn CgComponent>>,
|
pub elements: Vec<Box<&'a dyn CgComponent>>,
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
@@ -27,6 +26,9 @@ impl<'a> CgContainer<'a> {
|
|||||||
outlined,
|
outlined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn insert(&mut self, element: Box<&'a dyn CgComponent>) {
|
||||||
|
self.elements.push(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CgOutline for CgContainer<'_> {
|
impl CgOutline for CgContainer<'_> {
|
||||||
@@ -58,7 +60,7 @@ impl CgComponent for CgContainer<'_> {
|
|||||||
for widget in &self.elements {
|
for widget in &self.elements {
|
||||||
let frame = widget.render()?;
|
let frame = widget.render()?;
|
||||||
match result.render_bounds_check(&frame, true) { // TODO: this needs to be set to false for production
|
match result.render_bounds_check(&frame, true) { // TODO: this needs to be set to false for production
|
||||||
Ok(()) => result.render_element(&frame),
|
Ok(()) => result.place_child_element(&frame),
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +73,7 @@ impl CgComponent for CgContainer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgTextBox {
|
pub struct CgTextBox {
|
||||||
title: String,
|
title: String,
|
||||||
content: String,
|
content: String,
|
||||||
@@ -178,7 +180,7 @@ impl CgOutline for CgTextBox {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgLabel {
|
pub struct CgLabel {
|
||||||
content: String,
|
content: String,
|
||||||
position: Position,
|
position: Position,
|
||||||
@@ -193,6 +195,9 @@ impl CgLabel {
|
|||||||
dimensions: Dimensions::new(width, 1),
|
dimensions: Dimensions::new(width, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn set_text(&mut self, text: String) {
|
||||||
|
self.content = text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CgComponent for CgLabel {
|
impl CgComponent for CgLabel {
|
||||||
@@ -208,6 +213,7 @@ impl CgComponent for CgLabel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgIndicatorWidget {
|
pub struct CgIndicatorWidget {
|
||||||
content: String,
|
content: String,
|
||||||
colour: ColorCode,
|
colour: ColorCode,
|
||||||
@@ -256,6 +262,7 @@ impl CgComponent for CgIndicatorWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgIndicatorBar {
|
pub struct CgIndicatorBar {
|
||||||
pub fields: Vec<CgIndicatorWidget>,
|
pub fields: Vec<CgIndicatorWidget>,
|
||||||
position: Position,
|
position: Position,
|
||||||
@@ -288,7 +295,7 @@ impl CgComponent for CgIndicatorBar {
|
|||||||
width_idx += widget.len();
|
width_idx += widget.len();
|
||||||
|
|
||||||
match result.render_bounds_check(&frame, true) {
|
match result.render_bounds_check(&frame, true) {
|
||||||
Ok(()) => result.render_element(&frame),
|
Ok(()) => result.place_child_element(&frame),
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,6 +304,7 @@ impl CgComponent for CgIndicatorBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct CgStatusBar {
|
pub struct CgStatusBar {
|
||||||
position: Position,
|
position: Position,
|
||||||
dimensions: Dimensions,
|
dimensions: Dimensions,
|
||||||
@@ -321,10 +329,8 @@ impl CgComponent for CgStatusBar {
|
|||||||
let width = screen_mode.dimensions().x;
|
let width = screen_mode.dimensions().x;
|
||||||
screen_mode.set_position(Position::new(self.dimensions.x - width, 0));
|
screen_mode.set_position(Position::new(self.dimensions.x - width, 0));
|
||||||
|
|
||||||
frame.render_element(&window_title);
|
frame.place_child_element(&window_title);
|
||||||
frame.render_element(&screen_mode);
|
frame.place_child_element(&screen_mode);
|
||||||
|
|
||||||
serial_println!("{:?}", frame);
|
|
||||||
|
|
||||||
Ok(frame)
|
Ok(frame)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user