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:
FantasyPvP
2023-11-24 00:40:27 +00:00
parent 467a42a5fa
commit 140ac0ab32
13 changed files with 288 additions and 419 deletions
+4 -1
View File
@@ -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(())
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
}
}
+5 -5
View File
@@ -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,
-290
View File
@@ -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
}
+5 -5
View File
@@ -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);
}
+41 -12
View File
@@ -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
}
}
+15 -9
View File
@@ -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)
}