keyboard input

This commit is contained in:
2024-09-21 19:08:03 +02:00
parent f47f3575de
commit 56d1691397
5 changed files with 85 additions and 64 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "osui" name = "osui"
version = "0.0.1" version = "0.0.2"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
description = "A TUI library" description = "A TUI library"
@@ -14,4 +14,4 @@ name = "osui"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
k_board = { version = "1.2.1", features = ["standar", "f", "alt_lower_letter", "ctrl_lower_letter", "lower_letter"] } crossterm = "0.28.1"
+5 -5
View File
@@ -14,16 +14,16 @@ To use `osui` in your Rust project, add the following to your `Cargo.toml`:
```toml ```toml
[dependencies] [dependencies]
osui = "^0.0.1" osui = "^0.0.2"
``` ```
```rust ```rust
use osui::{self, ui}; use osui::{self, render_frame, ui};
fn main() { fn main() {
osui::clear(); let txt = ui::text("Hello World!");
let mut txt = ui::text("hello, world!"); render_frame(vec![txt.clone()]);
txt.render();
} }
``` ```
+53 -27
View File
@@ -1,5 +1,9 @@
use std::io::{self, Write}; use crossterm::terminal::{self, disable_raw_mode, enable_raw_mode};
use std::{
io::{self, Read, Write},
str::from_utf8,
};
pub mod key;
pub mod ui; pub mod ui;
#[derive(Clone)] #[derive(Clone)]
@@ -7,11 +11,10 @@ pub struct ComponentWrapper<T> {
pub component: T, pub component: T,
pub x: usize, pub x: usize,
pub y: usize, pub y: usize,
cleared_frame: String,
} }
pub trait Component { pub trait Component {
fn render(&mut self) -> String { fn render(&self) -> String {
String::new() String::new()
} }
fn run(&mut self, x: usize, y: usize) -> String { fn run(&mut self, x: usize, y: usize) -> String {
@@ -27,31 +30,8 @@ impl<T: Component> ComponentWrapper<T> {
component, component,
x: 0, x: 0,
y: 0, y: 0,
cleared_frame: String::new(),
} }
} }
pub fn render(&mut self) {
self.clean();
self.cleared_frame.clear();
for (i, d) in format!("{}", self.component.render())
.split("\n")
.enumerate()
{
print!("\x1B[{};{}H{}", self.y + (i + 1), self.x, d);
self.cleared_frame += format!(
"\x1B[{};{}H{}",
self.y + (i + 1),
self.x,
" ".repeat(d.len())
)
.as_str();
}
flush();
}
pub fn clean(&self) {
print!("{}", self.cleared_frame);
flush();
}
pub fn run(&mut self) -> String { pub fn run(&mut self) -> String {
self.component.run(self.x, self.y) self.component.run(self.x, self.y)
} }
@@ -65,3 +45,49 @@ pub fn clear() {
pub fn flush() { pub fn flush() {
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
} }
pub fn render_frame<T: Component>(components: Vec<ComponentWrapper<T>>) {
clear();
let (w, h) = terminal::size().unwrap();
let mut frame: Vec<String> = vec![" ".repeat(w as usize); h as usize - 1];
for c in components {
for (i, line) in c.component.render().split("\n").enumerate() {
frame[c.y + i] = render_line(frame[c.y + i].clone(), line.to_string(), c.x);
}
}
print!("{}", frame.join("\n"));
flush();
}
fn render_line(frame_line: String, line: String, x: usize) -> String {
let mut result = String::new();
if x >= frame_line.len() {
return result;
}
let lchars: Vec<char> = line.chars().collect();
for (i, c) in frame_line.chars().enumerate() {
if i >= x && lchars.len() > (i - x) {
result += lchars[i - x].to_string().as_str();
} else {
result += c.to_string().as_str();
}
}
result
}
fn read_key_() -> String {
let mut buf = [0u8; 2];
io::stdin().read(&mut buf).unwrap();
let mut res = from_utf8(&buf).unwrap().to_string();
if !res.ends_with("\0") {
res += read_key_().as_str()
}
return res.strip_suffix("\0").unwrap_or(&res).to_string();
}
pub fn read_key() -> String {
enable_raw_mode().unwrap();
let res = read_key_();
disable_raw_mode().unwrap();
return res;
}
+3 -4
View File
@@ -1,7 +1,6 @@
use osui::{self, ui}; use osui::{self, render_frame, ui};
fn main() { fn main() {
osui::clear(); let txt = ui::text("Hello World!");
let mut txt = ui::text("hello, world!"); render_frame(vec![txt.clone()]);
txt.render();
} }
+18 -22
View File
@@ -1,8 +1,6 @@
use crate::{Component, ComponentWrapper}; use crate::{flush, key, read_key, Component, ComponentWrapper};
use k_board::{keyboard::Keyboard, keys::Keys};
impl Component for String { impl Component for String {
fn render(&mut self) -> String { fn render(&self) -> String {
self.clone() self.clone()
} }
} }
@@ -11,51 +9,49 @@ pub fn text(text: &str) -> ComponentWrapper<String> {
ComponentWrapper::new(text.to_string()) ComponentWrapper::new(text.to_string())
} }
#[derive(Clone)]
pub struct Input { pub struct Input {
pub max_length: usize, pub max_length: usize,
} }
impl Component for Input { impl Component for Input {
fn render(&mut self) -> String { fn render(&self) -> String {
format!( format!(
" {}\n|{}|\n {}\x1b[1A\x1b[{}D", " {}\n|{}|\n {}",
"_".repeat(self.max_length), "_".repeat(self.max_length),
" ".repeat(self.max_length), " ".repeat(self.max_length),
"‾".repeat(self.max_length), "‾".repeat(self.max_length),
self.max_length
) )
} }
fn run(&mut self, x: usize, y: usize) -> String { fn run(&mut self, x: usize, y: usize) -> String {
print!("\x1B[{};{}H", y + 2, x + 2); print!("\x1B[{};{}H", y + 2, x + 2);
flush();
let mut input = String::new(); let mut input = String::new();
loop { loop {
let key = Keyboard::new().read_key(); match read_key().as_str() {
if key == Keys::Enter { key::ENTER => {
print!("\n\n");
return input; return input;
} }
if input.len() < self.max_length { key::BACKSPACE => {
match key {
Keys::Char(c) => {
input += c.to_string().as_str();
print!("{c}");
}
Keys::Space => {
input += " ";
print!(" ");
}
Keys::Delete => {
if input.len() > 0 { if input.len() > 0 {
input.pop(); input.pop();
print!("\x1b[1D \x1b[1D"); print!("\x1b[1D \x1b[1D");
flush();
} }
} }
_ => {} c => {
if input.len() < self.max_length {
input += c.to_string().as_str();
print!("{c}");
flush();
}
} }
} }
} }
} }
} }
pub fn input(max_length: usize) -> ComponentWrapper<Input> { pub fn input_box(max_length: usize) -> ComponentWrapper<Input> {
ComponentWrapper::new(Input { max_length }) ComponentWrapper::new(Input { max_length })
} }