- 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:
@@ -1,15 +1,12 @@
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::String,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
|
||||
use alloc::fmt::format;
|
||||
use crate::kernel::render::{ColorCode, RenderError};
|
||||
use crate::serial_println;
|
||||
use super::cg_core::{
|
||||
Position, Dimensions, ColouredChar, CgComponent, CgOutline, Frame, GuiError,
|
||||
CgComponent, CgOutline
|
||||
};
|
||||
|
||||
|
||||
use crate::std::frame::{ColouredChar, Dimensions, Position, Frame};
|
||||
use crate::std::io::Color;
|
||||
|
||||
|
||||
pub struct CgContainer {
|
||||
@@ -34,33 +31,33 @@ impl CgOutline for CgContainer {
|
||||
fn render_outline(&self, frame: &mut Frame) {
|
||||
// draws the sides of the container
|
||||
for i in 0..frame.dimensions.x {
|
||||
frame.set_pos(Position::new(i, 0), ColouredChar::white('─'));
|
||||
frame.set_pos(Position::new(i, frame.dimensions.y - 1), ColouredChar::white('─'));
|
||||
frame.write_pos(Position::new(i, 0), ColouredChar::new('─'));
|
||||
frame.write_pos(Position::new(i, frame.dimensions.y - 1), ColouredChar::new('─'));
|
||||
}
|
||||
|
||||
// draws the top and bottom of the container
|
||||
for i in 0..frame.dimensions.y {
|
||||
frame.set_pos(Position::new(0, i), ColouredChar::white('│'));
|
||||
frame.set_pos(Position::new(frame.dimensions.x - 1, i), ColouredChar::white('│'));
|
||||
frame.write_pos(Position::new(0, i), ColouredChar::new('│'));
|
||||
frame.write_pos(Position::new(frame.dimensions.x - 1, i), ColouredChar::new('│'));
|
||||
}
|
||||
|
||||
// draws the corners of the container
|
||||
frame.set_pos(Position::new(0, 0), ColouredChar::white('┌'));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, 0), ColouredChar::white('┐'));
|
||||
frame.set_pos(Position::new(0, self.dimensions.y - 1), ColouredChar::white('└'));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::white('┘'));
|
||||
frame.write_pos(Position::new(0, 0), ColouredChar::new('┌'));
|
||||
frame.write_pos(Position::new(self.dimensions.x - 1, 0), ColouredChar::new('┐'));
|
||||
frame.write_pos(Position::new(0, self.dimensions.y - 1), ColouredChar::new('└'));
|
||||
frame.write_pos(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::new('┘'));
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgContainer {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut result = Frame::new(self.position, self.dimensions)?;
|
||||
|
||||
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),
|
||||
Err(e) => return Err(GuiError::OutOfBounds(e)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,80 +76,100 @@ pub struct CgTextBox {
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
outlined: bool,
|
||||
wrap_words: bool // if false then will not wrap until the end of a word if possible
|
||||
}
|
||||
|
||||
impl CgTextBox {
|
||||
pub fn new(title: String, content: String, position: Position, dimensions: Dimensions, outlined: bool) -> CgTextBox {
|
||||
CgTextBox { title, content, position, dimensions, outlined }
|
||||
CgTextBox { title, content, position, dimensions, outlined, wrap_words: false }
|
||||
}
|
||||
|
||||
fn render_title(&self, frame: &mut Frame) {
|
||||
let title = self.title.chars();
|
||||
for (i, c) in title.enumerate() {
|
||||
if i + 2 == self.dimensions.x - 3 { // we dont want to write at the top of the text box
|
||||
frame.write_pos(Position::new(i + 1, 0), ColouredChar::new('.'));
|
||||
} else if i + 2 >= self.dimensions.x - 2 {
|
||||
frame.write_pos(Position::new(i + 1, 0), ColouredChar::new('.'));
|
||||
break;
|
||||
}
|
||||
frame.write_pos(Position::new(i + 2, 0), ColouredChar::new(c));
|
||||
}
|
||||
}
|
||||
pub fn wrap_words(&mut self, wrap: bool) {
|
||||
self.wrap_words = wrap;
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgTextBox {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut result = Frame::new(self.position, self.dimensions)?;
|
||||
|
||||
if self.outlined {
|
||||
self.render_outline(&mut result);
|
||||
}
|
||||
|
||||
let title = self.title.chars();
|
||||
for (i, c) in title.enumerate() {
|
||||
if i + 2 == self.dimensions.x - 3 { // we dont want to write at the top of the text box
|
||||
result.set_pos(Position::new(i + 1, 0), ColouredChar::white('.'));
|
||||
} else if i + 2 >= self.dimensions.x - 2 {
|
||||
result.set_pos(Position::new(i + 1, 0), ColouredChar::white('.'));
|
||||
break;
|
||||
self.render_title(&mut result);
|
||||
|
||||
let (mut x, mut y) = (1, 1);
|
||||
|
||||
for word in self.content.split(' ') {
|
||||
if self.wrap_words {
|
||||
if word.len() > self.dimensions.x - 2 - x {
|
||||
if word.len() <= self.dimensions.x - 2 {
|
||||
x = 1;
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.set_pos(Position::new(i + 2, 0), ColouredChar::white(c));
|
||||
|
||||
for c in format!("{} ", word).chars() {
|
||||
if x == self.dimensions.x - 1 {
|
||||
x = 1;
|
||||
y += 1;
|
||||
if c == ' ' {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if y == self.dimensions.y - 1 {
|
||||
if c != ' ' {
|
||||
(2..5).for_each(|z| {
|
||||
result.write_pos(Position::new(self.dimensions.x - z, self.dimensions.y - 1), ColouredChar::new('.'));
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
result.write_pos(Position::new(x, y), ColouredChar::new(c));
|
||||
x += 1;
|
||||
};
|
||||
}
|
||||
|
||||
let (mut x, mut y) = (1,1);
|
||||
|
||||
for c in self.content.chars() {
|
||||
if x == self.dimensions.x - 1 {
|
||||
x = 1;
|
||||
y += 1;
|
||||
if c == ' ' {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if y == self.dimensions.y - 1 {
|
||||
if c != ' ' {
|
||||
(2..5).for_each(|z| {
|
||||
result.set_pos(Position::new(self.dimensions.x - z, self.dimensions.y -1), ColouredChar::white('.'));
|
||||
})
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
result.set_pos(Position::new(x, y), ColouredChar::white(c));
|
||||
x += 1;
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl CgOutline for CgTextBox {
|
||||
fn render_outline(&self, frame: &mut Frame) {
|
||||
// draws the sides of the container
|
||||
for i in 0..frame.dimensions.x {
|
||||
frame.set_pos(Position::new(i, 0), ColouredChar::white('─'));
|
||||
frame.set_pos(Position::new(i, frame.dimensions.y - 1), ColouredChar::white('─'));
|
||||
frame.write_pos(Position::new(i, 0), ColouredChar::new('─'));
|
||||
frame.write_pos(Position::new(i, frame.dimensions.y - 1), ColouredChar::new('─'));
|
||||
}
|
||||
|
||||
// draws the top and bottom of the container
|
||||
for i in 0..frame.dimensions.y {
|
||||
frame.set_pos(Position::new(0, i), ColouredChar::white('│'));
|
||||
frame.set_pos(Position::new(frame.dimensions.x - 1, i), ColouredChar::white('│'));
|
||||
frame.write_pos(Position::new(0, i), ColouredChar::new('│'));
|
||||
frame.write_pos(Position::new(frame.dimensions.x - 1, i), ColouredChar::new('│'));
|
||||
}
|
||||
|
||||
// draws the corners of the container
|
||||
frame.set_pos(Position::new(0, 0), ColouredChar::white('┌'));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, 0), ColouredChar::white('┐'));
|
||||
frame.set_pos(Position::new(0, self.dimensions.y - 1), ColouredChar::white('└'));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::white('┘'));
|
||||
frame.write_pos(Position::new(0, 0), ColouredChar::new('┌'));
|
||||
frame.write_pos(Position::new(self.dimensions.x - 1, 0), ColouredChar::new('┐'));
|
||||
frame.write_pos(Position::new(0, self.dimensions.y - 1), ColouredChar::new('└'));
|
||||
frame.write_pos(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::new('┘'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,13 +194,13 @@ impl CgLabel {
|
||||
}
|
||||
|
||||
impl CgComponent for CgLabel {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut result = Frame::new(self.position, self.dimensions)?;
|
||||
|
||||
let shortened_string = self.content.chars().take(self.dimensions.x).collect::<String>();
|
||||
|
||||
for (i, c) in shortened_string.chars().enumerate() {
|
||||
result.set_pos(Position::new(i, 0), ColouredChar::white(c));
|
||||
result.write_pos(Position::new(i, 0), ColouredChar::new(c));
|
||||
};
|
||||
|
||||
serial_println!("{:?}", result);
|
||||
@@ -192,6 +209,90 @@ impl CgComponent for CgLabel {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CgIndicatorWidget {
|
||||
content: String,
|
||||
colour: ColorCode,
|
||||
visible: bool,
|
||||
max_width: usize,
|
||||
}
|
||||
impl CgIndicatorWidget {
|
||||
pub fn new(content: String, max_width: usize) -> CgIndicatorWidget {
|
||||
CgIndicatorWidget {
|
||||
content,
|
||||
visible: true,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
max_width
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_colour(&mut self, colour: ColorCode) {
|
||||
self.colour = colour;
|
||||
}
|
||||
fn visible(&mut self, visible: bool) {
|
||||
self.visible = visible;
|
||||
}
|
||||
fn len(&self) -> usize {
|
||||
self.max_width
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgIndicatorWidget {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
if !self.visible {
|
||||
return Ok(Frame::new(Position::new(0, 0), Dimensions::new(0, 0))?);
|
||||
}
|
||||
let mut result = Frame::new(Position::new(0, 0), Dimensions::new(self.max_width, 1))?;
|
||||
|
||||
let shortened_string = self.content.chars().take(self.max_width).collect::<String>();
|
||||
for (i, c) in shortened_string.chars().enumerate() {
|
||||
result.write_pos(Position::new(i, 0), ColouredChar::coloured(c, self.colour));
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CgIndicatorBar {
|
||||
pub(crate) fields: Vec<CgIndicatorWidget>,
|
||||
position: Position,
|
||||
dimensions: Dimensions,
|
||||
}
|
||||
|
||||
impl CgIndicatorBar {
|
||||
pub fn new(position: Position, width: usize) -> CgIndicatorBar {
|
||||
CgIndicatorBar {
|
||||
fields: Vec::new(),
|
||||
position: Position::new(position.x, position.y),
|
||||
dimensions: Dimensions::new(width, 1),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_field(&mut self, field: CgIndicatorWidget) {
|
||||
self.fields.push(field);
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgIndicatorBar {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut result = Frame::new(self.position, self.dimensions)?;
|
||||
|
||||
let mut width_idx = 0;
|
||||
|
||||
for widget in &self.fields {
|
||||
let mut frame = widget.render()?;
|
||||
frame.set_position(Position::new(width_idx, 0));
|
||||
width_idx += widget.len();
|
||||
|
||||
match result.render_bounds_check(&frame, true) {
|
||||
Ok(()) => result.render_element(&frame),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -221,11 +322,6 @@ impl CgComponent for CgLabel {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user