continued to work on new UI library

- implemented CgStatusBar widget which is a specific version of the CgIndicatorBar widget with predefined fields

- std::io::Screen is now an enum that makes switching between display modes more intuitive

- created a basic CgLineEdit implementation that allows for a user to type in a character and have it re-render that widget

- other more minor changes like fixes for existing apps to work with new features
This commit is contained in:
FantasyPvP
2023-11-23 20:29:51 +00:00
parent 461c9d9c6a
commit 467a42a5fa
14 changed files with 352 additions and 128 deletions
+16 -4
View File
@@ -38,7 +38,7 @@ pub struct ColorCode(u8);
impl ColorCode {
pub fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 5 | (foreground as u8))
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
@@ -207,11 +207,23 @@ impl Renderer {
self.temp_colour = None;
}
pub fn cursor_position(&mut self, x: u8, y: u8) -> Result<(), RenderError> {
// check that x and y are within bounds
if x >= 80 || x < 0 || y >= 25 || y < 0 {
return Err(RenderError::OutOfBounds(
x >= 80 || x < 0,
y >= 25 || y < 0
))
}
self.internal_set_cursor_position(x, y);
Ok(())
}
// INTERNAL API ONLY
fn set_cursor_position(&mut self, row: usize, col: usize) {
fn internal_set_cursor_position(&mut self, x: u8, y: u8) {
use x86_64::instructions::port::Port;
let cursor_position: u16 = (row as u16) * 80 + (col as u16);
let cursor_position: u16 = (y as u16) * 80 + (x as u16);
unsafe {// Write the high byte of the cursor position to register 14
let mut control_port = Port::<u8>::new(0x3D4);
@@ -302,7 +314,7 @@ impl Renderer {
}
}
}
self.set_cursor_position(BUFFER_HEIGHT - 1, self.col_pos);
self.internal_set_cursor_position(self.col_pos as u8, BUFFER_HEIGHT as u8 - 1);
}
}
+76 -17
View File
@@ -11,10 +11,12 @@ use core::{pin::Pin, task::{Poll, Context}};
use futures_util::stream::Stream;
use futures_util::task::AtomicWaker;
use futures_util::stream::StreamExt;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1, KeyCode};
use crate::print;
use crate::kernel::render::RENDERER;
use alloc::{string::String};
use core::ascii::Char;
use crate::kernel::tasks::keyboard::CharOrKeystroke::Char;
static WAKER: AtomicWaker = AtomicWaker::new();
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
@@ -29,6 +31,42 @@ pub struct KeyboardHandler {
keyboard: Keyboard<layouts::Uk105Key, ScancodeSet1>,
}
enum CharOrKeystroke {
Char(char),
Keystroke(KeyCode),
}
pub enum KeyStroke {
Char(char),
Ctrl,
RCtrl,
Alt,
RAlt,
Shift,
RShift,
Meta,
RMeta,
None,
}
impl KeyStroke {
pub fn from_keycode(key: KeyCode) -> KeyStroke {
match key {
KeyCode::ControlLeft => KeyStroke::Ctrl,
KeyCode::ControlRight => KeyStroke::RCtrl,
KeyCode::AltLeft => KeyStroke::Alt,
KeyCode::AltRight => KeyStroke::RAlt,
KeyCode::ShiftLeft => KeyStroke::Shift,
KeyCode::ShiftRight => KeyStroke::RShift,
KeyCode::WindowsLeft => KeyStroke::Meta,
KeyCode::WindowsRight => KeyStroke::RMeta,
_ => KeyStroke::None,
}
}
}
impl KeyboardHandler {
pub fn new() -> KeyboardHandler {
KeyboardHandler {
@@ -37,7 +75,7 @@ impl KeyboardHandler {
}
}
pub async fn get_keystroke_inner(&mut self) -> Option<char> {
pub async fn get_keystroke_inner(&mut self) -> Option<KeyStroke> {
loop {
if let Some(scancode) = self.scancodes.next().await {
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
@@ -50,10 +88,15 @@ impl KeyboardHandler {
});
return None;
} else {
return Some(character);
return Some(KeyStroke::Char(character));
}
},
DecodedKey::RawKey(key) => {
print!("{:?}", key)
match key {
KeyCode::NOn
}
},
DecodedKey::RawKey(key) => { print!("{:?}", key) },
}
}
}
@@ -61,16 +104,22 @@ impl KeyboardHandler {
}
}
pub async fn get_keystroke(&mut self) -> char {
pub async fn get_keystroke(&mut self) -> KeyStroke {
loop {
match self.get_keystroke_inner().await {
Some(c) => return c,
Some(c) => match c {
CharOrKeystroke::Char(c) => return KeyStroke::Char(c),
CharOrKeystroke::Keystroke(c) => match KeyStroke::from_keycode(c) {
KeyStroke::None => (),
key => return key
}
},
None => ()
}
}
}
pub fn try_keystroke(&mut self) -> Option<char> {
pub fn try_keystroke(&mut self) -> Option<KeyStroke> {
if let Some(scancode) = self.scancodes.try_next() {
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
if let Some(key) = self.keyboard.process_keyevent(key_event) {
@@ -82,10 +131,16 @@ impl KeyboardHandler {
});
return None;
} else {
return Some(character);
return Some(KeyStroke::Char(character));
}
},
DecodedKey::RawKey(key) => {
print!("{:?}", key);
match KeyStroke::from_keycode(key) {
KeyStroke::None => (),
key => return Some(key)
}
},
DecodedKey::RawKey(key) => { print!("{:?}", key) },
}
}
}
@@ -101,15 +156,19 @@ impl KeyboardHandler {
Some(c) => { c },
None => { val.pop(); continue; },
};
print!("{}", character);
let (character, execute): (char, bool) = match character {
'\n' => (character, true),
_ => (character, false),
};
val.push(character);
if execute {
return val;
if let CharOrKeystroke::Char(c) = character {
print!("{}", character);
let (c, execute): (char, bool) = match c {
'\n' => (c, true),
_ => (c, false),
};
val.push(c);
if execute {
return val;
}
}
}
}
+1 -1
View File
@@ -137,7 +137,7 @@ impl Frame {
pub fn dimensions(&self) -> Dimensions {
self.dimensions
}
pub fn write_pos(&mut self, position: Position, char: ColouredChar) {
pub fn write(&mut self, position: Position, char: ColouredChar) {
self.frame[position.y][position.x] = char
}
pub fn render_element(&mut self, other: &Frame) {
+27 -7
View File
@@ -1,6 +1,6 @@
use crate::{
kernel::render::{RENDERER, self},
kernel::tasks::keyboard::KEYBOARD,
kernel::tasks::keyboard::{KEYBOARD, KeyStroke},
};
use alloc::string::String;
@@ -12,19 +12,24 @@ use crate::kernel::serial::serial_reply;
use lazy_static::lazy_static;
use spin::Mutex;
use crate::kernel::render::Renderer;
use crate::std::frame::RenderError;
pub struct Stdin {}
impl Stdin {
/// waits for the user to type in a string and press enter | blocking
pub async fn readline() -> String {
let string = KEYBOARD.lock().get_string().await;
string
}
/// waits for a keystroke | blocking
pub async fn keystroke() -> char {
let chr = KEYBOARD.lock().get_keystroke().await;
chr
}
/// gets the next keystroke if any is present | non blocking
pub fn try_keystroke() -> Option<char> {
let chr = KEYBOARD.lock().try_keystroke();
chr
@@ -39,15 +44,30 @@ impl Serial {
}
}
pub struct Screen {}
/// enum with a terminal and application mode
pub enum Screen {
Terminal,
Application,
}
impl Screen {
pub fn terminal_mode() {
RENDERER.lock().terminal_mode().unwrap();
/// mode can be set for the kernel using this method
pub fn set_mode(&self) -> Result<(), RenderError> {
match self {
Screen::Terminal => RENDERER.lock().terminal_mode(),
Screen::Application => RENDERER.lock().application_mode(),
}
}
pub fn application_mode() {
RENDERER.lock().application_mode().unwrap();
/// returns the current display mode
pub fn get_mode() -> Screen {
match RENDERER.lock().mode_is_app() {
true => Screen::Application,
false => Screen::Terminal,
}
}
pub fn switch() {
/// switches between modes
pub fn switch(&self) {
if RENDERER.lock().mode_is_app() == true {
RENDERER.lock().terminal_mode().unwrap();
} else {