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
|
||||
self.term_buffer.push([ScreenChar::null(); BUFFER_WIDTH]);
|
||||
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
|
||||
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.col_pos = BUFFER_WIDTH;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use x86_64::instructions::interrupts;
|
||||
|
||||
use conquer_once::spin::OnceCell;
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use crate::println;
|
||||
use crate::{println, serial_println};
|
||||
|
||||
use core::{pin::Pin, task::{Poll, Context}};
|
||||
use futures_util::stream::Stream;
|
||||
@@ -15,8 +15,6 @@ use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1, Ke
|
||||
use crate::print;
|
||||
use crate::kernel::render::RENDERER;
|
||||
use alloc::{string::String};
|
||||
use core::ascii::Char;
|
||||
use crate::kernel::tasks::keyboard::CharOrKeystroke::Char;
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
||||
@@ -36,6 +34,7 @@ enum CharOrKeystroke {
|
||||
Keystroke(KeyCode),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum KeyStroke {
|
||||
Char(char),
|
||||
Ctrl,
|
||||
@@ -46,6 +45,9 @@ pub enum KeyStroke {
|
||||
RShift,
|
||||
Meta,
|
||||
RMeta,
|
||||
Backspace,
|
||||
Left,
|
||||
Right,
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -60,6 +62,9 @@ impl KeyStroke {
|
||||
KeyCode::ShiftRight => KeyStroke::RShift,
|
||||
KeyCode::WindowsLeft => KeyStroke::Meta,
|
||||
KeyCode::WindowsRight => KeyStroke::RMeta,
|
||||
KeyCode::Backspace => KeyStroke::Backspace,
|
||||
KeyCode::ArrowLeft => KeyStroke::Left,
|
||||
KeyCode::ArrowRight => KeyStroke::Right,
|
||||
_ => KeyStroke::None,
|
||||
}
|
||||
}
|
||||
@@ -81,20 +86,12 @@ impl KeyboardHandler {
|
||||
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
|
||||
if let Some(key) = self.keyboard.process_keyevent(key_event) {
|
||||
match key {
|
||||
DecodedKey::Unicode(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::Unicode(character) => return Some(KeyStroke::Char(character)),
|
||||
DecodedKey::RawKey(key) => {
|
||||
print!("{:?}", key)
|
||||
match key {
|
||||
KeyCode::NOn
|
||||
print!("{:?}", key);
|
||||
match KeyStroke::from_keycode(key) {
|
||||
KeyStroke::None => (),
|
||||
k => return Some(k)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -108,11 +105,8 @@ impl KeyboardHandler {
|
||||
loop {
|
||||
match self.get_keystroke_inner().await {
|
||||
Some(c) => match c {
|
||||
CharOrKeystroke::Char(c) => return KeyStroke::Char(c),
|
||||
CharOrKeystroke::Keystroke(c) => match KeyStroke::from_keycode(c) {
|
||||
KeyStroke::None => (),
|
||||
key => return key
|
||||
}
|
||||
KeyStroke::None => (),
|
||||
c => return c
|
||||
},
|
||||
None => ()
|
||||
}
|
||||
@@ -157,8 +151,16 @@ impl KeyboardHandler {
|
||||
None => { val.pop(); continue; },
|
||||
};
|
||||
|
||||
if let CharOrKeystroke::Char(c) = character {
|
||||
print!("{}", character);
|
||||
if let KeyStroke::Char(c) = 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 {
|
||||
'\n' => (c, true),
|
||||
_ => (c, false),
|
||||
|
||||
+10
-13
@@ -96,8 +96,7 @@ impl Frame {
|
||||
|
||||
for line in elemstr.split("\n") {
|
||||
element.frame.push(
|
||||
line
|
||||
.chars()
|
||||
line.chars()
|
||||
.map(|c| ColouredChar::new(c))
|
||||
.collect::<Vec<ColouredChar>>()
|
||||
);
|
||||
@@ -105,7 +104,7 @@ impl Frame {
|
||||
|
||||
for row in element.clone().frame {
|
||||
let n = row.len();
|
||||
if n > element.dimensions.x as usize {
|
||||
if n > element.dimensions.x {
|
||||
element.dimensions.x = n;
|
||||
}
|
||||
}
|
||||
@@ -116,21 +115,19 @@ impl Frame {
|
||||
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];
|
||||
for (i, row) in self.frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
//println!("{} {} {}", i, j, col);
|
||||
frame[i + self.position.y as usize][j + self.position.x as usize] = col.as_screen_char();
|
||||
frame[i + self.position.y][j + self.position.x] = col.as_screen_char();
|
||||
};
|
||||
}
|
||||
RENDERER.lock().render_frame(frame);
|
||||
Ok(())
|
||||
}
|
||||
pub fn position(&self) -> Position {
|
||||
pub fn get_position(&self) -> Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, position: Position) {
|
||||
self.position = position
|
||||
}
|
||||
@@ -140,7 +137,7 @@ impl Frame {
|
||||
pub fn write(&mut self, position: Position, char: ColouredChar) {
|
||||
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 (j, chr) in row.iter().enumerate() {
|
||||
self.frame[i + other.position.y][j + other.position.x] = *chr
|
||||
@@ -152,20 +149,20 @@ impl Frame {
|
||||
|
||||
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!(
|
||||
"Element is to large to be rendered {} {}",
|
||||
element.dimensions().x + element.position().x,
|
||||
element.dimensions().x + element.get_position().x,
|
||||
self.dimensions.x
|
||||
)} else {
|
||||
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!(
|
||||
"Element is to large to be rendered {} {}",
|
||||
element.dimensions().y + element.position().y,
|
||||
element.dimensions().y + element.get_position().y,
|
||||
self.dimensions.y
|
||||
)} else {
|
||||
y = true;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
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::vec::Vec;
|
||||
@@ -17,6 +18,7 @@ use crate::std::frame::RenderError;
|
||||
|
||||
pub struct Stdin {}
|
||||
impl Stdin {
|
||||
pub const BACKSPACE: char = b'\x08' as char;
|
||||
/// waits for the user to type in a string and press enter | blocking
|
||||
pub async fn readline() -> String {
|
||||
let string = KEYBOARD.lock().get_string().await;
|
||||
@@ -24,13 +26,13 @@ impl Stdin {
|
||||
}
|
||||
|
||||
/// waits for a keystroke | blocking
|
||||
pub async fn keystroke() -> char {
|
||||
pub async fn keystroke() -> KeyStroke {
|
||||
let chr = KEYBOARD.lock().get_keystroke().await;
|
||||
chr
|
||||
}
|
||||
|
||||
/// 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();
|
||||
chr
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::{
|
||||
Error,
|
||||
},
|
||||
};
|
||||
use crate::std::io::{KeyStroke, Stdin};
|
||||
|
||||
pub struct GameLoop;
|
||||
|
||||
@@ -98,7 +99,9 @@ impl Application for GameLoop {
|
||||
|
||||
|
||||
loop {
|
||||
println!("{}", io::Stdin::keystroke().await)
|
||||
if let KeyStroke::Char(c) = Stdin::keystroke().await {
|
||||
println!("{}", c)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -8,7 +8,7 @@ use async_trait::async_trait;
|
||||
use crate::kernel::render::{Color, ColorCode};
|
||||
use crate::{println, serial_println};
|
||||
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::user::bin::snake::Game;
|
||||
|
||||
@@ -75,7 +75,7 @@ impl GameOfLife {
|
||||
|
||||
self.render().map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?;
|
||||
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> {
|
||||
self.frame.render_to_screen()?;
|
||||
self.frame.write_to_screen()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+106
-31
@@ -4,13 +4,18 @@ use alloc::vec::Vec;
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use crate::{println, serial_println};
|
||||
use crate::kernel::render::RenderError;
|
||||
use crate::shell::command_handler;
|
||||
use crate::std::application::{Application, Error};
|
||||
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_Y: i64 = 12;
|
||||
const OFFSET_X: i64 = 39;
|
||||
const OFFSET_Y: i64 = 10;
|
||||
|
||||
pub struct Grapher {
|
||||
points: Vec<PointF64>,
|
||||
@@ -32,15 +37,86 @@ impl Application for Grapher {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
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> {
|
||||
let mut equation: String = args.into_iter().collect();
|
||||
use super::calc;
|
||||
Screen::Application.set_mode().map_err(|_| Error::ApplicationError(String::from("failed to set application mode")))?;
|
||||
|
||||
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();
|
||||
|
||||
for x in -4000..4000 {
|
||||
let x = x as f64 / 100.0;
|
||||
|
||||
@@ -48,8 +124,7 @@ impl Application for Grapher {
|
||||
if c == 'x' { format!("({})", x) } else { c.to_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 {
|
||||
self.render_point(PointF64 {
|
||||
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) {
|
||||
|
||||
let point = PointI64 {
|
||||
x: point.x 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;
|
||||
}
|
||||
|
||||
let offset_x = point.x + OFFSET_X;
|
||||
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, 24-offset_y as usize), ColouredChar::new('*'));
|
||||
self.frame.write(Position::new(offset_x as usize, 22-offset_y as usize), ColouredChar::new('*'));
|
||||
}
|
||||
|
||||
|
||||
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::kernel::render::ColorCode;
|
||||
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::user::bin::gigachad_detector::GigachadDetector;
|
||||
use crate::user::bin::grapher::Grapher;
|
||||
@@ -17,6 +17,7 @@ use crate::user::lib::libgui::{
|
||||
cg_core::{CgComponent},
|
||||
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_widgets::{CgIndicatorBar, CgIndicatorWidget, CgLabel, CgStatusBar};
|
||||
|
||||
@@ -190,8 +191,8 @@ async fn exec() -> Result<(), Error> {
|
||||
"test_features" => {
|
||||
Screen::Application.set_mode().unwrap();
|
||||
|
||||
new_eventloop(|c| match c {
|
||||
'x' => ('x', true),
|
||||
setup_ui(|c| match c {
|
||||
KeyStroke::Char('x') => (c, true),
|
||||
_ => (c, false),
|
||||
}).await;
|
||||
|
||||
@@ -259,7 +260,7 @@ struct CmdHistory {
|
||||
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(
|
||||
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,
|
||||
);
|
||||
|
||||
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 textedit = CgLineEdit::new(
|
||||
Position::new(0, 1),
|
||||
Position::new(0, 20),
|
||||
80,
|
||||
String::from("enter text here >"),
|
||||
);
|
||||
textedit.text = "hello".chars().collect();
|
||||
|
||||
let mut container = CgContainer::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));
|
||||
let mut commandresult = String::new();
|
||||
|
||||
while let (c, false) = input(Stdin::keystroke().await) {
|
||||
|
||||
|
||||
textedit.text.push(c);
|
||||
|
||||
let mut container = CgContainer::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));
|
||||
match c {
|
||||
KeyStroke::Char('\n') => {
|
||||
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() {
|
||||
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::cell::RefCell;
|
||||
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::kernel::tasks::keyboard::KEYBOARD;
|
||||
use crossbeam_queue::SegQueue;
|
||||
@@ -138,7 +138,7 @@ impl Game {
|
||||
// loop triggers when game is lost
|
||||
loop {
|
||||
match Stdin::keystroke().await {
|
||||
'x' => break 'gameloop,
|
||||
KeyStroke::Char('x') => break 'gameloop,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ impl Game {
|
||||
}
|
||||
});
|
||||
|
||||
frame.render_to_screen()?;
|
||||
frame.write_to_screen()?;
|
||||
|
||||
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[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(())
|
||||
}
|
||||
}
|
||||
@@ -272,7 +272,7 @@ impl Snake {
|
||||
if self.ai_controlled {
|
||||
self.dir = PathFinder::decide(&self.head, tails, points_of_interest);
|
||||
} else {
|
||||
if let Some(c) = Stdin::try_keystroke() {
|
||||
if let Some(KeyStroke::Char(c)) = Stdin::try_keystroke() {
|
||||
self.dir = match c {
|
||||
'w' => Direction::Degrees0,
|
||||
'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>;
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
fn write_char(&self) -> Result<Frame, RenderError>; // this can also be implemented in a way that inserts characters
|
||||
fn delete_char(&self) -> Result<Frame, RenderError>;
|
||||
fn move_cursor(&self, direction: bool) -> Result<(), RenderError>; // true = right, false = left
|
||||
fn clear(&self) -> Result<Frame, RenderError>;
|
||||
fn write_char(&mut self, c: char); // this can also be implemented in a way that inserts characters
|
||||
fn backspace(&mut self);
|
||||
fn move_cursor(&mut self, direction: bool); // true = right, false = left
|
||||
fn clear(&mut self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use alloc::vec::Vec;
|
||||
use crate::std::frame::{ColouredChar, Dimensions, Frame, Position, RenderError};
|
||||
use crate::user::lib::libgui::cg_core::{CgComponent, CgTextEdit};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgLineEdit {
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
@@ -26,28 +27,56 @@ impl CgLineEdit {
|
||||
impl CgComponent for CgLineEdit {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut frame = Frame::new(self.position, self.dimensions)?;
|
||||
|
||||
let mut ptr = 0;
|
||||
let mut idx = 0;
|
||||
|
||||
for c in self.prompt.chars() {
|
||||
if ptr >= self.dimensions.x {
|
||||
if idx >= self.dimensions.x {
|
||||
break;
|
||||
}
|
||||
frame.write(Position::new(ptr, 0), ColouredChar::new(c));
|
||||
ptr += 1
|
||||
frame.write(Position::new(idx, 0), ColouredChar::new(c));
|
||||
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 ptr >= self.dimensions.x {
|
||||
break;
|
||||
}
|
||||
frame.write(Position::new(ptr, 0), ColouredChar::new(*c));
|
||||
ptr += 1
|
||||
if idx + self.text.len() > self.dimensions.x {
|
||||
frame.write(Position::new(idx, 0), ColouredChar::new('['));
|
||||
frame.write(Position::new(idx + 1, 0), ColouredChar::new('.'));
|
||||
frame.write(Position::new(idx + 2, 0), ColouredChar::new('.'));
|
||||
frame.write(Position::new(idx + 3, 0), ColouredChar::new('.'));
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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::io::Color;
|
||||
|
||||
|
||||
pub struct CgContainer<'a> {
|
||||
pub elements: Vec<Box<&'a dyn CgComponent>>,
|
||||
pub position: Position,
|
||||
@@ -27,6 +26,9 @@ impl<'a> CgContainer<'a> {
|
||||
outlined,
|
||||
}
|
||||
}
|
||||
pub fn insert(&mut self, element: Box<&'a dyn CgComponent>) {
|
||||
self.elements.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
impl CgOutline for CgContainer<'_> {
|
||||
@@ -58,7 +60,7 @@ impl CgComponent for CgContainer<'_> {
|
||||
for widget in &self.elements {
|
||||
let frame = widget.render()?;
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -71,7 +73,7 @@ impl CgComponent for CgContainer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgTextBox {
|
||||
title: String,
|
||||
content: String,
|
||||
@@ -178,7 +180,7 @@ impl CgOutline for CgTextBox {
|
||||
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgLabel {
|
||||
content: String,
|
||||
position: Position,
|
||||
@@ -193,6 +195,9 @@ impl CgLabel {
|
||||
dimensions: Dimensions::new(width, 1),
|
||||
}
|
||||
}
|
||||
pub fn set_text(&mut self, text: String) {
|
||||
self.content = text;
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgLabel {
|
||||
@@ -208,6 +213,7 @@ impl CgComponent for CgLabel {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgIndicatorWidget {
|
||||
content: String,
|
||||
colour: ColorCode,
|
||||
@@ -256,6 +262,7 @@ impl CgComponent for CgIndicatorWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgIndicatorBar {
|
||||
pub fields: Vec<CgIndicatorWidget>,
|
||||
position: Position,
|
||||
@@ -288,7 +295,7 @@ impl CgComponent for CgIndicatorBar {
|
||||
width_idx += widget.len();
|
||||
|
||||
match result.render_bounds_check(&frame, true) {
|
||||
Ok(()) => result.render_element(&frame),
|
||||
Ok(()) => result.place_child_element(&frame),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
@@ -297,6 +304,7 @@ impl CgComponent for CgIndicatorBar {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CgStatusBar {
|
||||
position: Position,
|
||||
dimensions: Dimensions,
|
||||
@@ -321,10 +329,8 @@ impl CgComponent for CgStatusBar {
|
||||
let width = screen_mode.dimensions().x;
|
||||
screen_mode.set_position(Position::new(self.dimensions.x - width, 0));
|
||||
|
||||
frame.render_element(&window_title);
|
||||
frame.render_element(&screen_mode);
|
||||
|
||||
serial_println!("{:?}", frame);
|
||||
frame.place_child_element(&window_title);
|
||||
frame.place_child_element(&screen_mode);
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user