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:
FantasyPvP
2023-11-22 21:07:00 +00:00
parent 1d08240981
commit 84aaa90e66
12 changed files with 421 additions and 123 deletions
+47 -11
View File
@@ -13,8 +13,12 @@ use crate::std::io::{Color, write, Screen, Stdin, Serial};
use crate::std::random::Random;
use crate::user::bin::gigachad_detector::GigachadDetector;
use crate::user::bin::grapher::Grapher;
use crate::user::lib::gui_v2;
use crate::user::lib::gui_v2::widgets::GuiComponent;
use crate::user::lib::libgui;
use crate::user::lib::libgui::{
cg_core::{Frame, Position, Dimensions, CgComponent},
cg_widgets::{CgTextBox, CgContainer},
};
use crate::user::lib::libgui::cg_widgets::CgLabel;
lazy_static! {
pub static ref CMD: Mutex<CommandHandler> = Mutex::new(CommandHandler::new());
@@ -181,23 +185,55 @@ async fn exec() -> Result<(), Error> {
timer();
}
"test_features" => {
std::io::Screen::application_mode();
let mut container = gui_v2::widgets::Container::new(
gui_v2::widgets::Position::new(2, 2),
gui_v2::widgets::Dimensions::new(10, 10),
let textbox = CgTextBox::new(
String::from("i'd just like to interject for a moment"),
String::from("I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called Linux, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux!"),
Position::new(2, 2),
Dimensions::new(40, 12),
true,
);
let res = container.render().unwrap().render();
let mut elem = ColouredElement {
frame: res as Vec<Vec<ScreenChar>>,
dimensions: (container.dimensions.x as u8, container.dimensions.y as u8),
let textbox2 = CgTextBox::new(
String::from("i'd just like to interject for a moment"),
String::from("I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called Linux, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux!"),
Position::new(10, 5),
Dimensions::new(40, 12),
true,
);
let mut label = CgLabel::new(
String::from("i'd just like to interject for a moment"),
Position::new(5, 16),
15,
);
let mut container = CgContainer::new(
Position::new(2, 2),
Dimensions::new(65, 20),
true,
);
container.elements.push(Box::new(textbox));
container.elements.push(Box::new(textbox2));
container.elements.push(Box::new(label));
let res = if let Ok(frame) = container.render() {
frame
} else {
return Err(Error::CommandFailed("failed to render frame".to_string()))
};
elem.render((2, 2)).unwrap();
let mut elem = ColouredElement::generate(
res.render_screen_char(),
(res.dimensions().x as u8, res.dimensions().y as u8),
);
elem.render((0, 0)).unwrap();
//std::io::Screen::terminal_mode();
}
_ => {
+1 -1
View File
@@ -8,7 +8,7 @@ use crate::{serial_print, serial_println};
use crate::std::application::{Application, Error};
use crate::std::io::Screen;
use crate::user::lib::coords::{Direction, Position, PositionReal};
use crate::user::lib::libgui::libgui_core::Pos;
use crate::user::lib::libgui_old_archive::libgui_core::Pos;
pub(crate) struct TetrisEngine {
-2
View File
@@ -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)
}
}
+234
View File
@@ -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)
}
}
+3 -2
View File
@@ -1,2 +1,3 @@
pub mod libgui_core;
pub mod libgui_elements;
pub mod cg_core;
pub(crate) mod cg_widgets;
+2
View File
@@ -0,0 +1,2 @@
pub mod libgui_core;
pub mod libgui_elements;
+2 -2
View File
@@ -1,3 +1,3 @@
pub mod libgui;
pub mod libgui_old_archive;
pub mod coords;
pub mod gui_v2;
pub mod libgui;