new gui module text box implementation
starting to work on the new UI library, got labels and text boxes working as well as containers for widgets. planning a big refactor
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
pub mod widgets;
|
||||
|
||||
@@ -3,11 +3,13 @@ use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::slice::from_mut;
|
||||
use crate::kernel::render::ScreenChar;
|
||||
use crate::printerr;
|
||||
use crate::user::lib::gui_v2::widgets::XorY::Both;
|
||||
use crate::kernel::render::{ColorCode, ScreenChar};
|
||||
use crate::{printerr, serial_println};
|
||||
use crate::std::frame::special_char;
|
||||
use crate::std::io::Color;
|
||||
use crate::user::lib::libgui::cg_core::XorY::Both;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Position {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
@@ -52,12 +54,50 @@ pub enum GuiError {
|
||||
}
|
||||
|
||||
pub type Dimensions = Position;
|
||||
pub type ColouredChar = ScreenChar;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ColouredChar {
|
||||
pub character: char,
|
||||
pub colour: ColorCode,
|
||||
}
|
||||
|
||||
impl ColouredChar {
|
||||
pub fn new(character: char, colour: ColorCode) -> ColouredChar {
|
||||
ColouredChar {
|
||||
character,
|
||||
colour,
|
||||
}
|
||||
}
|
||||
pub fn white(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(Clone, Debug)]
|
||||
pub struct Frame {
|
||||
position: Position,
|
||||
dimensions: Dimensions,
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
frame: Vec<Vec<ColouredChar>>,
|
||||
}
|
||||
|
||||
@@ -66,12 +106,20 @@ impl Frame {
|
||||
Ok(Frame {
|
||||
position,
|
||||
dimensions,
|
||||
frame: vec![vec![ScreenChar::null(); dimensions.x]; dimensions.y],
|
||||
frame: vec![vec![ColouredChar::null(); dimensions.x]; dimensions.y],
|
||||
})
|
||||
}
|
||||
pub fn render(&self) -> Vec<Vec<ColouredChar>> {
|
||||
self.frame.clone()
|
||||
}
|
||||
|
||||
pub fn render_screen_char(&self) -> Vec<Vec<ScreenChar>> {
|
||||
self.frame.clone().into_iter().map(|row| {
|
||||
row.into_iter().map(|char| {
|
||||
char.as_screen_char()
|
||||
}).collect::<Vec<ScreenChar>>()
|
||||
}).collect::<Vec<Vec<ScreenChar>>>()
|
||||
}
|
||||
pub fn position(&self) -> Position {
|
||||
self.position
|
||||
}
|
||||
@@ -82,11 +130,27 @@ impl Frame {
|
||||
self.frame[position.y][position.x] = char
|
||||
}
|
||||
pub fn render_element(&mut self, other: &Frame) {
|
||||
serial_println!("frame:\n{}",
|
||||
other.frame.iter().map(|x| {
|
||||
x.iter().map(|y| {
|
||||
y.character
|
||||
}).collect::<String>()
|
||||
}).collect::<Vec<String>>().join("\n")
|
||||
);
|
||||
|
||||
for (i, row) in other.frame.iter().enumerate() {
|
||||
for (j, chr) in row.iter().enumerate() {
|
||||
self.frame[i + self.position.y][j + self.position.x] = *chr
|
||||
self.frame[i + other.position.y][j + other.position.x] = *chr
|
||||
}
|
||||
}
|
||||
|
||||
serial_println!("self:\n{}",
|
||||
self.frame.iter().map(|x| {
|
||||
x.iter().map(|y| {
|
||||
y.character
|
||||
}).collect::<String>()
|
||||
}).collect::<Vec<String>>().join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render_bounds_check(&self, element: &Frame, should_panic: bool) -> Result<(), XorY> {
|
||||
@@ -127,73 +191,18 @@ impl Frame {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CgOutline {
|
||||
fn render_outline(&self, frame: &mut Frame);
|
||||
}
|
||||
|
||||
pub trait GuiComponent {
|
||||
|
||||
pub trait CgComponent {
|
||||
fn render(&self) -> Result<Frame, GuiError>;
|
||||
}
|
||||
|
||||
|
||||
pub struct Container {
|
||||
pub frame: Vec<Vec<ColouredChar>>,
|
||||
pub elements: Vec<Box<dyn GuiComponent>>,
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
pub outlined: bool,
|
||||
}
|
||||
|
||||
impl Container {
|
||||
|
||||
pub fn new(position: Position, dimensions: Dimensions, outlined: bool) -> Container {
|
||||
Container {
|
||||
frame: vec![vec![ScreenChar::null(); dimensions.x]; dimensions.y],
|
||||
elements: Vec::new(),
|
||||
position,
|
||||
dimensions,
|
||||
outlined,
|
||||
}
|
||||
}
|
||||
|
||||
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('│' as u8));
|
||||
frame.set_pos(Position::new(i, frame.dimensions.y - 1), ColouredChar::white('│' as u8));
|
||||
}
|
||||
|
||||
// draws the top and bottom of the container
|
||||
for i in 0..frame.dimensions.y {
|
||||
frame.set_pos(Position::new(0, i), ColouredChar::white('─' as u8));
|
||||
frame.set_pos(Position::new(frame.dimensions.x - 1, i), ColouredChar::white('─' as u8));
|
||||
}
|
||||
|
||||
// draws the corners of the container
|
||||
frame.set_pos(Position::new(0, 0), ColouredChar::white('┌' as u8));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, 0), ColouredChar::white('┐' as u8));
|
||||
frame.set_pos(Position::new(0, self.dimensions.y - 1), ColouredChar::white('└' as u8));
|
||||
frame.set_pos(Position::new(self.dimensions.x - 1, self.dimensions.y - 1), ColouredChar::white('┘' as u8));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl GuiComponent for Container {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
if self.outlined {
|
||||
self.render_outline(&mut result);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::String,
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use crate::serial_println;
|
||||
use super::cg_core::{
|
||||
Position, Dimensions, ColouredChar, CgComponent, CgOutline, Frame, GuiError,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct CgContainer {
|
||||
pub elements: Vec<Box<dyn CgComponent>>,
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
pub outlined: bool,
|
||||
}
|
||||
|
||||
impl CgContainer {
|
||||
pub fn new(position: Position, dimensions: Dimensions, outlined: bool) -> CgContainer {
|
||||
CgContainer {
|
||||
elements: Vec::new(),
|
||||
position,
|
||||
dimensions,
|
||||
outlined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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('─'));
|
||||
}
|
||||
|
||||
// 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('│'));
|
||||
}
|
||||
|
||||
// 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('┘'));
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgContainer {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
if self.outlined {
|
||||
self.render_outline(&mut result);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct CgTextBox {
|
||||
title: String,
|
||||
content: String,
|
||||
pub position: Position,
|
||||
pub dimensions: Dimensions,
|
||||
outlined: bool,
|
||||
}
|
||||
|
||||
impl CgTextBox {
|
||||
pub fn new(title: String, content: String, position: Position, dimensions: Dimensions, outlined: bool) -> CgTextBox {
|
||||
CgTextBox { title, content, position, dimensions, outlined }
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgTextBox {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
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;
|
||||
|
||||
}
|
||||
result.set_pos(Position::new(i + 2, 0), ColouredChar::white(c));
|
||||
}
|
||||
|
||||
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('─'));
|
||||
}
|
||||
|
||||
// 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('│'));
|
||||
}
|
||||
|
||||
// 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('┘'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct CgLabel {
|
||||
content: String,
|
||||
position: Position,
|
||||
dimensions: Dimensions,
|
||||
}
|
||||
|
||||
impl CgLabel {
|
||||
pub fn new(content: String, position: Position, width: usize) -> CgLabel {
|
||||
CgLabel {
|
||||
content,
|
||||
position: Position::new(position.x, position.y),
|
||||
dimensions: Dimensions::new(width, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for CgLabel {
|
||||
fn render(&self) -> Result<Frame, GuiError> {
|
||||
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));
|
||||
};
|
||||
|
||||
serial_println!("{:?}", result);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod libgui_core;
|
||||
pub mod libgui_elements;
|
||||
pub mod cg_core;
|
||||
pub(crate) mod cg_widgets;
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod libgui_core;
|
||||
pub mod libgui_elements;
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
pub mod libgui;
|
||||
pub mod libgui_old_archive;
|
||||
pub mod coords;
|
||||
pub mod gui_v2;
|
||||
pub mod libgui;
|
||||
|
||||
Reference in New Issue
Block a user