- got text boxes fully working
- this includes text wrapping not cutting words in half (can be disabled using a method on the text box) - refactored frame.rs, cg_core.rs and cg_widgets.rs to avoid code reuse and duplication - created a simplified unified interface for rendering frames to the screen using the Frame struct provided by frame.rs instead of Element, FrameGen, etc. - moved all widgets from cg_core.rs to cg_widgets.rs - the label widget now works - also added CgIndicatorBar and CgIndicatorWidget widgets to eventually make a working status bar - refactored all applications in the system to use the new api to render to the screen
This commit is contained in:
+15
-13
@@ -6,6 +6,7 @@ use volatile::Volatile;
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use crate::kernel::render::RenderError::InvalidRenderMode;
|
||||
use crate::serial_println;
|
||||
use crate::std::io::Screen;
|
||||
|
||||
@@ -48,6 +49,14 @@ pub struct ScreenChar {
|
||||
pub colour: ColorCode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RenderError {
|
||||
OutOfBounds(bool, bool), // (bool, bool) refers to x and y respectively
|
||||
InvalidCharacter,
|
||||
InvalidColour,
|
||||
InvalidRenderMode,
|
||||
}
|
||||
|
||||
impl ScreenChar {
|
||||
pub fn null() -> ScreenChar {
|
||||
ScreenChar {
|
||||
@@ -108,7 +117,6 @@ impl Renderer {
|
||||
pub fn render_frame(&mut self, frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT]) { // renders the given frame to the app buffer
|
||||
let mut processed_frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
||||
|
||||
|
||||
for (i, row) in frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
processed_frame[i][j] = match special_char(col.character as char) {
|
||||
@@ -128,9 +136,9 @@ impl Renderer {
|
||||
self.internal_render();
|
||||
}
|
||||
|
||||
pub fn application_mode(&mut self) -> Result<(), ()> {
|
||||
pub fn application_mode(&mut self) -> Result<(), RenderError> {
|
||||
if self.application_mode {
|
||||
return Err(());
|
||||
return Err(InvalidRenderMode);
|
||||
} else {
|
||||
self.application_mode = true;
|
||||
self.internal_render();
|
||||
@@ -138,9 +146,9 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminal_mode(&mut self) -> Result<(), ()> {
|
||||
pub fn terminal_mode(&mut self) -> Result<(), RenderError> {
|
||||
if !self.application_mode {
|
||||
return Err(());
|
||||
return Err(InvalidRenderMode);
|
||||
} else {
|
||||
self.application_mode = false;
|
||||
self.internal_render();
|
||||
@@ -172,7 +180,7 @@ impl Renderer {
|
||||
self.internal_render();
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self) -> Result<(), ()> {
|
||||
pub fn backspace(&mut self) -> Result<(), RenderError> {
|
||||
if self.application_mode { return Ok(()); };
|
||||
|
||||
loop {
|
||||
@@ -218,7 +226,7 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_backspace(&mut self) -> Result<bool, ()> {
|
||||
fn internal_backspace(&mut self) -> Result<bool, RenderError> {
|
||||
let mut should_break = false;
|
||||
|
||||
if self.col_pos == 0 {
|
||||
@@ -327,12 +335,6 @@ pub fn special_char(ch: char) -> Option<u8> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
impl fmt::Write for Renderer {
|
||||
fn write_str(&mut self, string: &str) -> fmt::Result {
|
||||
self.write_string(string, None);
|
||||
|
||||
+190
-121
@@ -1,9 +1,11 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER, ScreenChar};
|
||||
use crate::println;
|
||||
use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, ColorCode, RENDERER, ScreenChar};
|
||||
use crate::{println, serial_println};
|
||||
use spin::Mutex;
|
||||
use crate::std::io::{Color, Screen};
|
||||
|
||||
/// TODO: get a working implementation for CLI apps
|
||||
/// elements can be created using their from_str() method
|
||||
@@ -13,145 +15,212 @@ use spin::Mutex;
|
||||
/// nothing will appear on the screen until the frame is actually rendered by
|
||||
/// the render_frame method on the renderer
|
||||
|
||||
pub use crate::system::kernel::render::special_char;
|
||||
pub use crate::system::kernel::render::{special_char, RenderError};
|
||||
|
||||
|
||||
pub type Frame = [ [ ScreenChar; BUFFER_WIDTH ]; BUFFER_HEIGHT];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Element {
|
||||
frame: Vec<Vec<char>>,
|
||||
dimensions: (u8, u8)
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ColouredChar {
|
||||
pub character: char,
|
||||
pub colour: ColorCode,
|
||||
}
|
||||
|
||||
impl Element {
|
||||
impl ColouredChar {
|
||||
pub fn coloured(character: char, colour: ColorCode) -> ColouredChar {
|
||||
ColouredChar { character, colour }
|
||||
}
|
||||
pub fn new(character: char) -> ColouredChar {
|
||||
ColouredChar {
|
||||
character,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}
|
||||
}
|
||||
pub fn null() -> ColouredChar {
|
||||
ColouredChar {
|
||||
character: ' ',
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
}
|
||||
}
|
||||
pub fn as_screen_char(&self) -> ScreenChar {
|
||||
ScreenChar {
|
||||
character: {
|
||||
if let Some(c) = special_char(self.character) {
|
||||
c
|
||||
} else {
|
||||
self.character as u8
|
||||
}
|
||||
},
|
||||
colour: self.colour,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Position {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(x: usize, y: usize) -> Position {
|
||||
Position { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
pub type Dimensions = Position;
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Frame {
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
pub frame: Vec<Vec<ColouredChar>>,
|
||||
}
|
||||
|
||||
|
||||
impl Frame {
|
||||
pub fn new(position: Position, dimensions: Dimensions) -> Result<Frame, RenderError> {
|
||||
Ok(Frame {
|
||||
position,
|
||||
dimensions,
|
||||
frame: vec![vec![ColouredChar::null(); dimensions.x]; dimensions.y],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_str(elemstr: String) -> Self {
|
||||
let mut element = Element { frame: Vec::<Vec<char>>::new(), dimensions: (0, 0) };
|
||||
let mut element = Frame { frame: Vec::<Vec<ColouredChar>>::new(), dimensions: Dimensions::new(0, 0), position: Position::new(0, 0) };
|
||||
|
||||
for line in elemstr.split("\n") {
|
||||
let mut ln = Vec::<char>::new();
|
||||
for col in line.chars() {
|
||||
ln.push(col)
|
||||
};
|
||||
element.frame.push(ln);
|
||||
element.frame.push(
|
||||
line
|
||||
.chars()
|
||||
.map(|c| ColouredChar::new(c))
|
||||
.collect::<Vec<ColouredChar>>()
|
||||
);
|
||||
}
|
||||
|
||||
for row in element.clone().frame {
|
||||
let n = row.len();
|
||||
if n > element.dimensions.0 as usize {
|
||||
element.dimensions.0 = n as u8;
|
||||
if n > element.dimensions.x as usize {
|
||||
element.dimensions.x = n;
|
||||
}
|
||||
}
|
||||
element
|
||||
}
|
||||
|
||||
pub fn generate(frame: Vec::<Vec<char>>, dims: (u8, u8)) -> Self {
|
||||
Element { frame, dimensions: dims }
|
||||
pub fn render(&self) -> Vec<Vec<ColouredChar>> {
|
||||
self.frame.clone()
|
||||
}
|
||||
|
||||
pub fn render(&mut self, pos: (u8, u8)) { // x,y
|
||||
for (i, row) in self.frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
//println!("{} {} {}", i, j, col);
|
||||
FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = ScreenChar::white(*col as u8);
|
||||
};
|
||||
}
|
||||
FRAMEGEN.lock().render_frame();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ColouredElement {
|
||||
pub frame: Vec<Vec<ScreenChar>>,
|
||||
pub dimensions: (u8, u8)
|
||||
}
|
||||
|
||||
impl ColouredElement {
|
||||
pub fn from_str(elemstr: String) -> Self {
|
||||
let mut element = ColouredElement { frame: Vec::<Vec<ScreenChar>>::new(), dimensions: (0, 0) };
|
||||
|
||||
for line in elemstr.split("\n") {
|
||||
let mut ln = Vec::<ScreenChar>::new();
|
||||
for col in line.chars() {
|
||||
ln.push(ScreenChar::white(col as u8))
|
||||
};
|
||||
element.frame.push(ln);
|
||||
}
|
||||
|
||||
for row in element.clone().frame {
|
||||
let n = row.len();
|
||||
if n > element.dimensions.0 as usize {
|
||||
element.dimensions.0 = n as u8;
|
||||
}
|
||||
}
|
||||
element
|
||||
}
|
||||
|
||||
pub fn generate(frame: Vec::<Vec<ScreenChar>>, dims: (u8, u8)) -> Self {
|
||||
ColouredElement { frame, dimensions: dims }
|
||||
}
|
||||
|
||||
pub fn render(&mut self, pos: (u8, u8)) -> Result<(), ()> { // x,y
|
||||
|
||||
// this block returns an error if any characters will be drawn out of the bounds of the screen
|
||||
if self.dimensions.0 + pos.0 > BUFFER_WIDTH as u8 {
|
||||
return Err(());
|
||||
} else if self.dimensions.1 + pos.1 > BUFFER_HEIGHT as u8 {
|
||||
return Err(());
|
||||
} else if self.frame.len() != self.dimensions.1 as usize {
|
||||
return Err(())
|
||||
} else if self.frame.iter().map(|r| r.len()).max().ok_or_else(|| ())? > self.dimensions.0 as usize {
|
||||
return Err(())
|
||||
}
|
||||
|
||||
for (i, row) in self.frame.iter().enumerate() {
|
||||
for (j, col) in row.iter().enumerate() {
|
||||
//println!("{} {} {}", i, j, col);
|
||||
FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = *col;
|
||||
};
|
||||
}
|
||||
FRAMEGEN.lock().render_frame();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FrameGen {
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
|
||||
impl FrameGen {
|
||||
pub fn render_frame(&self) {
|
||||
RENDERER.lock().render_frame(self.frame)
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
pub fn render_to_screen(&self) -> Result<(), RenderError> {
|
||||
let mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
||||
Self { frame: Frame::from(frame) }
|
||||
}
|
||||
|
||||
fn set_frame(&mut self, frame: Frame) {
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
pub fn get_frame(&self) -> &[ [ ScreenChar; BUFFER_WIDTH ]; BUFFER_HEIGHT] {
|
||||
&self.frame
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref FRAMEGEN: Mutex<FrameGen> = Mutex::new(FrameGen::new() );
|
||||
}
|
||||
|
||||
|
||||
impl core::fmt::Display for FrameGen {
|
||||
fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
println!(" ");
|
||||
for row in &self.frame {
|
||||
println!("{}", row.iter().map(|c| c.character as char ).collect::<String>());
|
||||
};
|
||||
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();
|
||||
};
|
||||
}
|
||||
RENDERER.lock().render_frame(frame);
|
||||
Ok(())
|
||||
}
|
||||
pub fn position(&self) -> Position {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, position: Position) {
|
||||
self.position = position
|
||||
}
|
||||
pub fn dimensions(&self) -> Dimensions {
|
||||
self.dimensions
|
||||
}
|
||||
pub fn write_pos(&mut self, position: Position, char: ColouredChar) {
|
||||
self.frame[position.y][position.x] = char
|
||||
}
|
||||
pub fn render_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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_bounds_check(&self, element: &Frame, should_panic: bool) -> Result<(), RenderError> {
|
||||
|
||||
let (mut x, mut y) = (false, false);
|
||||
|
||||
if element.dimensions().x + element.position().x > self.dimensions.x {
|
||||
if should_panic { panic!(
|
||||
"Element is to large to be rendered {} {}",
|
||||
element.dimensions().x + element.position().x,
|
||||
self.dimensions.x
|
||||
)} else {
|
||||
x = true;
|
||||
}
|
||||
}
|
||||
|
||||
if element.dimensions().y + element.position().y > self.dimensions.y {
|
||||
if should_panic { panic!(
|
||||
"Element is to large to be rendered {} {}",
|
||||
element.dimensions().y + element.position().y,
|
||||
self.dimensions.y
|
||||
)} else {
|
||||
y = true;
|
||||
}
|
||||
}
|
||||
|
||||
if x || y {
|
||||
Err(RenderError::OutOfBounds(x, y))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Index<usize> for Frame {
|
||||
type Output = Vec<ColouredChar>;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.frame[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::IndexMut<usize> for Frame {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.frame[index]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user