working buttons and div

This commit is contained in:
2024-10-18 18:04:25 +02:00
parent d093debfd5
commit 83b9a66515
5 changed files with 171 additions and 103 deletions
+106 -26
View File
@@ -1,44 +1,124 @@
use std::collections::HashMap;
use crate::{
create_frame, key::KeyKind, utils::render_to_frame, Component, Params, UpdateContext,
UpdateRequest,
create_frame,
key::{Key, KeyKind},
utils::{closest_component, render_to_frame},
Component, Params, UpdateResponse,
};
pub fn div(params: Params) -> Component {
let mut c = Component::new(params);
c.render = |s: &mut Component| {
let mut component = Component::new(params);
component.binds = HashMap::from([
(KeyKind::Up, String::from("up")),
(KeyKind::Down, String::from("down")),
(KeyKind::Left, String::from("left")),
(KeyKind::Right, String::from("right")),
]);
fn update(this: &mut Component, k: Key) -> UpdateResponse {
if let Some(v) = this.binds.get(&k.kind) {
match v.as_str() {
"up" => {
this.active_child = closest_component(
&this.children,
this.active_child,
crate::utils::Direction::Up,
);
}
"down" => {
this.active_child = closest_component(
&this.children,
this.active_child,
crate::utils::Direction::Down,
);
}
"left" => {
this.active_child = closest_component(
&this.children,
this.active_child,
crate::utils::Direction::Left,
);
}
"right" => {
this.active_child = closest_component(
&this.children,
this.active_child,
crate::utils::Direction::Right,
);
}
_ => {
if let Some(child) = this.get_active_child() {
return (child.update)(child, k);
}
}
}
} else {
if let Some(child) = this.get_active_child() {
return (child.update)(child, k);
}
}
UpdateResponse::None
}
fn render(s: &mut Component) -> String {
let mut frame: Vec<String> = create_frame!(s.width, s.height);
for c in &mut s.children {
render_to_frame(&mut frame, c);
}
frame.join("\n")
};
c
}
component.update = update;
component.render = render;
component
}
pub fn text(params: Params) -> Component {
let mut c = Component::new(params);
c.render = |s: &mut Component| s.expr.clone();
c
let mut component = Component::new(params);
fn update(_: &mut Component, _: Key) -> UpdateResponse {
UpdateResponse::None
}
fn render(s: &mut Component) -> String {
s.expr.clone()
}
component.update = update;
component.render = render;
component
}
pub fn button(params: Params) -> Component {
let mut c = Component::new(params);
c.update = |s: &mut Component, ctx: &mut UpdateContext| {
s.expr = "nah".to_string();
match ctx.request.clone() {
UpdateRequest::Key(k) => {
if k.kind == KeyKind::Enter {
s.clicked = !s.clicked;
let mut cctx = UpdateContext {
request: UpdateRequest::Key(k),
response: crate::UpdateResponse::None,
};
(s.on_click)(&mut cctx);
ctx.response = cctx.response;
}
let mut component = Component::new(params);
fn update(this: &mut Component, k: Key) -> UpdateResponse {
if k.kind == KeyKind::Enter {
if this.toggle {
this.clicked = !this.clicked;
(this.on_click)(this);
} else {
this.clicked = true;
(this.on_click)(this);
this.clicked = false;
(this.on_click)(this);
}
}
};
c.render = |s: &mut Component| s.expr.clone();
c
UpdateResponse::None
}
fn render(s: &mut Component) -> String {
s.expr.clone()
}
component.update = update;
component.render = render;
component
}
+4 -42
View File
@@ -1,6 +1,6 @@
use std::io::{self, Read};
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum KeyKind {
Enter,
Escape,
@@ -11,18 +11,14 @@ pub enum KeyKind {
Char(String),
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Key {
pub kind: KeyKind,
pub raw: String,
pub ctrl: bool,
pub shift: bool,
}
impl Key {
pub fn new(k: String) -> Key {
let mut ctrl = false;
let mut shift = false;
let kind = match k.as_str() {
"\r" => KeyKind::Enter,
"\x1b" => KeyKind::Escape,
@@ -31,43 +27,9 @@ impl Key {
"\x1b[C" => KeyKind::Right,
"\x1b[D" => KeyKind::Left,
// Ctrl keys
"\u{17}" => {
ctrl = true;
KeyKind::Char("w".to_string())
}
"\u{13}" => {
ctrl = true;
KeyKind::Char("s".to_string())
}
"\u{1}" => {
ctrl = true;
KeyKind::Char("a".to_string())
}
"\u{4}" => {
ctrl = true;
KeyKind::Char("d".to_string())
}
"\u{11}" => {
ctrl = true;
KeyKind::Char("q".to_string())
}
_ => {
shift = k.to_uppercase() == k;
KeyKind::Char(k.clone())
}
_ => KeyKind::Char(k.clone()),
};
Key {
raw: k,
kind,
ctrl,
shift,
}
Key { raw: k, kind }
}
}
+22 -29
View File
@@ -4,27 +4,13 @@ pub mod macros;
pub mod utils;
use crossterm;
use key::Key;
use std::collections::HashMap;
use utils::{clear, show_cursor, Value};
#[derive(Debug, Clone, PartialEq)]
pub enum UpdateResponse {
Exit,
Done,
None,
SetComponent(Component),
}
#[derive(Debug, Clone)]
pub enum UpdateRequest {
Key(Key),
}
#[derive(Debug, Clone)]
pub struct UpdateContext {
pub request: UpdateRequest,
pub response: UpdateResponse,
}
#[derive(Debug)]
@@ -36,17 +22,18 @@ pub struct Params {
#[derive(Debug, Clone, PartialEq)]
pub struct Component {
pub render: fn(&mut Component) -> String,
pub update: fn(&mut Component, &mut UpdateContext),
pub on_click: fn(&mut UpdateContext),
pub update: fn(&mut Component, key::Key) -> UpdateResponse,
pub on_click: fn(&mut Component),
pub x: usize,
pub y: usize,
pub width: usize,
pub height: usize,
pub expr: String,
pub children: Vec<Component>,
pub active_child: usize,
pub clicked: bool,
pub toggle: bool,
pub data: HashMap<String, Value>,
pub binds: HashMap<key::KeyKind, String>,
}
impl Component {
@@ -54,7 +41,7 @@ impl Component {
fn new(params: Params) -> Component {
Component {
render: |_| String::new(),
update: |_, _| {},
update: |_, _| UpdateResponse::None,
on_click: |_| {},
x: 0,
y: 0,
@@ -62,11 +49,23 @@ impl Component {
height: 0,
expr: params.expr,
children: params.children,
active_child: 0,
clicked: false,
toggle: false,
data: HashMap::new(),
binds: HashMap::new(),
}
}
pub fn get_active_child(&mut self) -> Option<&mut Component> {
if self.active_child < self.children.len() {
return self.children.get_mut(self.active_child);
}
None
}
fn manhattan_distance(&self, other: &Component) -> usize {
self.x.abs_diff(other.x) + self.y.abs_diff(other.y)
}
}
#[derive(Debug, Clone)]
@@ -95,7 +94,6 @@ impl App {
if self.component.height == 0 {
self.component.height = height as usize;
}
self.render();
}
/// Render to the screen
@@ -115,19 +113,14 @@ impl App {
crossterm::terminal::enable_raw_mode().unwrap();
loop {
self.render();
let mut ctx = UpdateContext {
response: UpdateResponse::None,
request: UpdateRequest::Key(key::read_key()),
};
(self.component.update)(&mut self.component, &mut ctx);
match ctx.response {
self.component.expr = "Hello".to_string();
match (self.component.update)(&mut self.component, key::read_key()) {
UpdateResponse::Exit => {
crossterm::terminal::disable_raw_mode().unwrap();
clear();
show_cursor();
utils::clear();
utils::show_cursor();
return;
}
UpdateResponse::SetComponent(c) => self.component = c,
_ => {}
}
}
+11 -6
View File
@@ -1,6 +1,6 @@
use osui::{
components::{button, div},
oml, App, Component, Params, UpdateResponse,
oml, App, Component, Params,
};
fn main() {
@@ -9,14 +9,19 @@ fn main() {
app.run();
}
fn onclick(c: &mut Component) {
if c.clicked {
c.expr = "Clicked!".to_string();
} else {
c.expr = "click me".to_string()
}
}
fn app_elem(_: Params) -> Component {
oml!(
div {
button("click me"; on_click = |c| {
c.response = UpdateResponse::SetComponent(
oml!(button("Clicked!";))
);
})
button("click me"; on_click = onclick)
button("click me"; y = 2, on_click = onclick)
}
)
}
+28
View File
@@ -100,3 +100,31 @@ pub fn show_cursor() {
pub fn flush() {
stdout().flush().unwrap();
}
pub enum Direction {
Left,
Right,
Up,
Down,
}
pub fn closest_component(
components: &[Component],
current_index: usize,
direction: Direction,
) -> usize {
let current = &components[current_index];
components
.iter()
.enumerate() // Keep track of indices
.filter(|(_, comp)| match direction {
Direction::Left => comp.x < current.x && comp.y == current.y, // Left
Direction::Right => comp.x > current.x && comp.y == current.y, // Right
Direction::Up => comp.y < current.y && comp.x == current.x, // Up
Direction::Down => comp.y > current.y && comp.x == current.x, // Down
})
.min_by_key(|(_, comp)| current.manhattan_distance(comp)) // Find the closest component
.map(|(index, _)| index) // Return the index of the closest component
.unwrap_or(current_index) // If no component is found, return the current index
}