working buttons and div
This commit is contained in:
+104
-24
@@ -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) => {
|
||||
let mut component = Component::new(params);
|
||||
|
||||
fn update(this: &mut Component, k: Key) -> UpdateResponse {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
UpdateResponse::None
|
||||
}
|
||||
};
|
||||
c.render = |s: &mut Component| s.expr.clone();
|
||||
c
|
||||
|
||||
fn render(s: &mut Component) -> String {
|
||||
s.expr.clone()
|
||||
}
|
||||
|
||||
component.update = update;
|
||||
component.render = render;
|
||||
component
|
||||
}
|
||||
|
||||
+4
-42
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user