updated a few things

This commit is contained in:
2024-11-17 17:01:48 +01:00
parent 74767cf961
commit aa9d68f16f
11 changed files with 325 additions and 277 deletions
+1
View File
@@ -19,3 +19,4 @@ crossterm = "0.28.1"
regex = "*" regex = "*"
lazy_static = "1.4" lazy_static = "1.4"
paste = "1.0.15" paste = "1.0.15"
osui-element = { path = "./osui-element" }
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "osui-element"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
path = "./lib.rs"
[dependencies]
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
proc-macro2 = "*"
+125
View File
@@ -0,0 +1,125 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse::Parser, parse_macro_input, DeriveInput, Fields, LifetimeParam};
#[proc_macro_attribute]
pub fn element(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
match &mut ast.data {
syn::Data::Struct(ref mut struct_data) => {
if ast.generics.lifetimes().count() == 0 {
ast.generics
.params
.push(syn::GenericParam::Lifetime(LifetimeParam::new(
syn::Lifetime::new("'a", proc_macro2::Span::call_site()),
)));
}
if let Fields::Named(fields) = &mut struct_data.fields {
// Example fields, modify according to your needs
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub x: Value<usize> })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub y: usize })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub width: Value<usize> })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub height: Value<usize> })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub children: Children })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub id: &'a str })
.unwrap(),
);
fields.named.push(
syn::Field::parse_named
.parse2(quote! { pub class: &'a str })
.unwrap(),
);
}
}
_ => panic!("`element` can only be used with structs"),
}
let expanded_impl = quote! {
impl<'a> ElementComponent for #struct_name<'a> {
fn get_data(&self) -> (Value<usize>, usize, String) {
(self.x, self.y, self.id.to_string())
}
fn update_data(&mut self, width: usize, height: usize) {
self.width.try_set_value(width);
self.height.try_set_value(height);
if let Children::Children(children, _) = &mut self.children {
for child in children {
child.update_data(width, height);
}
}
}
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn Element>> {
if let Children::Children(children, _) = &mut self.children {
for elem in children {
if elem.get_data().2 == id {
return Some(elem);
}
}
}
None
}
fn get_child(&mut self) -> Option<&mut Box<dyn Element>> {
if let Children::Children(children, child) = &mut self.children {
children.get_mut(*child)
} else {
None
}
}
}
};
let expanded = quote! {
#ast
#expanded_impl
};
expanded.into()
}
#[proc_macro_attribute]
pub fn elem_fn(_args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
let func_name = syn::Ident::new(
&struct_name.to_string().to_lowercase(),
proc_macro2::Span::call_site(),
);
let elem_fn = quote! {
pub fn #func_name<'a>() -> Box<#struct_name<'a>> {
Box::new(#struct_name::default())
}
};
let expanded = quote! {
#ast
#elem_fn
};
expanded.into()
}
+4 -2
View File
@@ -11,6 +11,7 @@ typedef struct {
extern void render(void* element, uint32_t state); extern void render(void* element, uint32_t state);
extern void event(void* element, uint32_t* state); extern void event(void* element, uint32_t* state);
extern void init_event(void* element, uint32_t* state);
void sleep_ms(long milliseconds) { void sleep_ms(long milliseconds) {
struct timespec ts; struct timespec ts;
@@ -22,6 +23,7 @@ void sleep_ms(long milliseconds) {
void *event_checker(void *args) void *event_checker(void *args)
{ {
EventArgs* event_args = (EventArgs*)args; EventArgs* event_args = (EventArgs*)args;
init_event(event_args->element, event_args->state);
while (1) { while (1) {
if (event_args->state == 0) { return NULL; } if (event_args->state == 0) { return NULL; }
event(event_args->element, event_args->state); event(event_args->element, event_args->state);
@@ -32,7 +34,7 @@ void *event_checker(void *args)
boolean_t c_run(void* element) boolean_t c_run(void* element)
{ {
pthread_t thread; pthread_t thread;
uint32_t state = 2; uint32_t state = 3;
EventArgs event_args = { .element = element, .state = &state }; EventArgs event_args = { .element = element, .state = &state };
if (pthread_create(&thread, NULL, event_checker, &event_args) != 0) { if (pthread_create(&thread, NULL, event_checker, &event_args) != 0) {
@@ -49,6 +51,6 @@ boolean_t c_run(void* element)
return 1; return 1;
} }
render(element, state); render(element, state);
sleep_ms(200); sleep_ms(20);
} }
} }
+25 -4
View File
@@ -1,6 +1,9 @@
use crossterm::terminal::enable_raw_mode; use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crate::{clear, create_frame, flush, get_term_size, render_to_frame, Element, State, StateChanger, Value}; use crate::{
utils::{clear, create_frame, flush, get_term_size, hide_cursor, render_to_frame, show_cursor}, Element,
State, StateChanger,
};
use std::ffi::c_void; use std::ffi::c_void;
pub struct ElementWrapper<'a> { pub struct ElementWrapper<'a> {
@@ -27,7 +30,7 @@ extern "C" {
pub extern "C" fn render(ptr: *mut c_void, state: u32) { pub extern "C" fn render(ptr: *mut c_void, state: u32) {
let element = unsafe { ElementWrapper::from_raw(ptr) }; let element = unsafe { ElementWrapper::from_raw(ptr) };
let (width, height) = get_term_size(); let (width, height) = get_term_size();
let mut frame: Vec<String> = create_frame(Value::new(width), Value::new(height)); let mut frame: Vec<String> = create_frame(width, height);
render_to_frame(State::from_u32(state), width, &mut frame, element); render_to_frame(State::from_u32(state), width, &mut frame, element);
clear(); clear();
print!("{}", frame.join("")); print!("{}", frame.join(""));
@@ -38,13 +41,31 @@ pub extern "C" fn render(ptr: *mut c_void, state: u32) {
pub extern "C" fn event(ptr: *mut c_void, state: *mut u32) { pub extern "C" fn event(ptr: *mut c_void, state: *mut u32) {
let event = crossterm::event::read().unwrap(); let event = crossterm::event::read().unwrap();
let element = unsafe { ElementWrapper::from_raw(ptr) }; let element = unsafe { ElementWrapper::from_raw(ptr) };
if let crossterm::event::Event::Resize(width, height) = event {
element.update_data(width as usize, height as usize);
}
element.event(event, &StateChanger(state));
}
#[no_mangle]
pub extern "C" fn init_event(ptr: *mut c_void, state: *mut u32) {
let event = crossterm::event::Event::FocusGained;
let element = unsafe { ElementWrapper::from_raw(ptr) };
let (width, height) = get_term_size();
element.update_data(width, height);
element.event(event, &StateChanger(state)); element.event(event, &StateChanger(state));
} }
pub fn run(data: &mut Box<dyn Element>) -> bool { pub fn run(data: &mut Box<dyn Element>) -> bool {
hide_cursor();
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();
clear();
unsafe { unsafe {
let element_ptr = ElementWrapper::new(data); let element_ptr = ElementWrapper::new(data);
return c_run(element_ptr); let c = c_run(element_ptr);
show_cursor();
disable_raw_mode().unwrap();
clear();
return c;
} }
} }
+38 -38
View File
@@ -16,20 +16,15 @@
//! ``` //! ```
//! //!
//! ## Modules //! ## Modules
//! - `utils` - Utility functions for common TUI tasks such as clearing the screen. //! - `app` - The main function for rendering.
//! - `ui` - Contains all user interface components, enabling rich CLI experiences. //! - `ui` - Contains all user interface components, enabling rich CLI experiences.
//! - `utils` - Utility functions for common TUI tasks such as clearing the screen.
pub mod app;
pub mod macros; pub mod macros;
pub mod ui; pub mod ui;
pub mod utils; pub mod utils;
pub use utils::*;
pub mod app;
/// Enum representing the wether it's the default or custom.
///
/// - `Default(usize)` - Uses a default value.
/// - `Custom(usize)` - Allows specifying a custom value.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Value<T: Copy + PartialEq> { pub enum Value<T: Copy + PartialEq> {
Default(T), Default(T),
@@ -37,20 +32,6 @@ pub enum Value<T: Copy + PartialEq> {
} }
impl<T: Copy + PartialEq> Value<T> { impl<T: Copy + PartialEq> Value<T> {
/// Creates a new Value.
///
/// # Returns
///
/// Value::Custom(usize)
pub fn new(value: T) -> Value<T> {
Value::Custom(value)
}
/// Retrieves the value.
///
/// # Returns
///
/// The value by the type T.
pub fn get_value(&self) -> T { pub fn get_value(&self) -> T {
match self { match self {
Value::Custom(s) => *s, Value::Custom(s) => *s,
@@ -58,13 +39,6 @@ impl<T: Copy + PartialEq> Value<T> {
} }
} }
/// Attempts to set the value if it is currently set to `Default`.
///
/// If the size is `Custom`, this function will not modify it.
///
/// # Arguments
///
/// * `value` - The value to change to.
pub fn try_set_value(&mut self, value: T) { pub fn try_set_value(&mut self, value: T) {
if let Value::Default(_) = *self { if let Value::Default(_) = *self {
*self = Value::Default(value); *self = Value::Default(value);
@@ -72,16 +46,20 @@ impl<T: Copy + PartialEq> Value<T> {
} }
} }
/// A trait for defining UI elements in OSUI. impl<T: Copy + PartialEq + Default> Default for Value<T> {
/// fn default() -> Self {
/// Elements must implement methods for updating and rendering data. Self::Default(T::default())
pub trait Element: Send + Sync { }
}
pub trait ElementComponent: Send + Sync {
fn get_data(&self) -> (Value<usize>, usize, String); fn get_data(&self) -> (Value<usize>, usize, String);
fn update_data(&mut self, width: usize, height: usize); fn update_data(&mut self, width: usize, height: usize);
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn Element>>; fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn Element>>;
fn get_child(&mut self) -> Option<&mut Box<dyn Element>>;
}
pub trait Element: std::fmt::Debug + Send + Sync + ElementComponent {
fn render(&self, state: State) -> String { fn render(&self, state: State) -> String {
_ = state; _ = state;
String::new() String::new()
@@ -92,18 +70,18 @@ pub trait Element: Send + Sync {
} }
} }
pub struct Handler<T>(pub Box<dyn FnMut(&T, &StateChanger, crossterm::event::Event) + Send>); pub struct Handler<T>(pub Box<dyn FnMut(&T, crossterm::event::Event, &StateChanger) + Send>);
impl<T> Handler<T> { impl<T> Handler<T> {
pub fn new<F>(handler_fn: F) -> Handler<T> pub fn new<F>(handler_fn: F) -> Handler<T>
where where
F: FnMut(&T, &StateChanger, crossterm::event::Event) + 'static + Send, F: FnMut(&T, crossterm::event::Event, &StateChanger) + 'static + Send,
{ {
Handler(Box::new(handler_fn)) Handler(Box::new(handler_fn))
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum State { pub enum State {
Exit, Exit,
Rebuild, Rebuild,
@@ -145,3 +123,25 @@ impl StateChanger {
State::from_u32(unsafe { *self.0 }) State::from_u32(unsafe { *self.0 })
} }
} }
#[derive(Debug)]
pub enum Children {
None,
Text(String),
Children(Vec<Box<dyn Element>>, usize),
}
impl Default for Children {
fn default() -> Self {
Self::None
}
}
impl Children {
pub fn is_none(&self) -> bool {
match self {
Children::None => true,
_ => false,
}
}
}
+15 -198
View File
@@ -1,180 +1,3 @@
//! # Macros Module
//!
//! This module defines various macros for creating and manipulating OSUI elements.
//! The macros provide a clean and concise syntax for defining UI components and
//! handling their parameters, commands, and rendering.
/// Macro for defining an OSUI `Element`.
///
/// This macro generates a new element type with specified parameters and default values.
///
/// # Example
/// ```
/// element! {
/// MyElem {}
/// defaults {}
/// // functions (render, update)
/// }
/// ```
///
/// # Parameters
/// - `name`: The name of the element type being defined.
/// - `inner`: Additional fields or methods for the element.
/// - `defaults`: Default values for the element's properties.
/// - `functions`: Functions to implement for the element (e.g., rendering, updating).
#[macro_export]
macro_rules! element {
(
$(#[$meta_style:meta])*
style $style:ident {
$($sn:ident: $st:ty),*$(,)?
}
$(#[$meta:meta])*
$name:ident {
$( $inner:tt )*
}
defaults {$( $defaults:tt )*}
$( $functions:tt )*
) => {
$(#[$meta_style])*
#[derive(Clone)]
pub struct $style {
pub color: Color,
pub background: Color,
pub font: Font,
pub hover_color: Color,
pub hover_background: Color,
pub hover_font: Font,
$(pub $sn: $st),*
}
impl Default for $style {
fn default() -> $style {
$style {
color: Color::default(),
background: Color::default(),
font: Font::default(),
hover_color: Color::default(),
hover_background: Color::default(),
hover_font: Font::default(),
$($sn: <$st>::default()),*
}
}
}
element! {
$(#[$meta])*
$name {
pub style: $style,
$( $inner )*
}
defaults {
style: $style::default(),
$($defaults)*
}
$( $functions )*
}
};
(
$(#[$meta:meta])*
$name:ident {
$( $inner:tt )*
}
defaults {$( $defaults:tt )*}
$( $functions:tt )*
) => {
$(#[$meta])*
pub struct $name<'a> {
pub x: $crate::Value<usize>,
pub y: usize,
pub width: $crate::Value<usize>,
pub height: $crate::Value<usize>,
pub children: Vec<Box<dyn $crate::Element>>,
pub child: usize,
pub text: String,
pub id: &'a str,
$( $inner )*
}
impl $crate::Element for $name<'_> {
fn get_data(&self) -> ($crate::Value<usize>, usize, String) {
(self.x, self.y, self.id.to_string())
}
fn update_data(&mut self, width: usize, height: usize) {
self.width.try_set_value(width);
self.height.try_set_value(height);
for child in &mut self.children {
child.update_data(width, height);
}
}
fn get_element_by_id(&mut self, id: &str) -> Option<&mut Box<dyn $crate::Element>> {
for elem in &mut self.children {
if elem.get_data().2 == id {
return Some(elem);
}
}
None
}
$( $functions )*
}
impl<'a> $name<'_> {
/// Creates a new instance of the element with default values.
pub fn new() -> $name<'a> {
$name {
children: Vec::new(),
x: $crate::Value::Default(0),
y: 0,
width: $crate::Value::Default(0),
height: $crate::Value::Default(0),
child: 0,
text: String::new(),
id: "",
$( $defaults )*
}
}
/// Retrieves a mutable reference to the current child element, if any.
///
/// # Returns
/// An `Option` containing a mutable reference to the child `Element` or `None`.
pub fn get_child(&mut self) -> Option<&mut Box<dyn $crate::Element>> {
self.children.get_mut(self.child)
}
}
};
}
#[macro_export]
macro_rules! def_ {
($name:ident { $($k:ident : $v:expr),* $(,)? }) => {
$name {
$(
$k : std::sync::Mutex::new($v)
),*
}
};
}
/// Macro for creating a command response from a list of commands.
///
/// # Example
/// ```
/// command!(Update(1), Render(2));
/// ```
///
/// # Parameters
/// - A list of commands to be included in the `EventResponse::CommandList`.
#[macro_export]
macro_rules! command {
($($cmd:expr),*) => {
EventResponse::CommandList(vec![$($cmd),*])
};
}
/// Macro for parsing parameters and children for an OSUI element. /// Macro for parsing parameters and children for an OSUI element.
/// ///
/// This macro updates the properties of an element based on provided key-value pairs, /// This macro updates the properties of an element based on provided key-value pairs,
@@ -219,30 +42,39 @@ macro_rules! parse_rsx_param {
// For loop // For loop
($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => { ($elem:expr, for ($($for:tt)*) { $($inner:tt)* } $($rest:tt)*) => {
if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
if let $crate::Children::Children(children, _) = &mut $elem.children {
for $($for)* { for $($for)* {
$elem.children.push({$($inner)*}) children.push({$($inner)*})
}
} }
osui::parse_rsx_param!($elem, $($rest)*); osui::parse_rsx_param!($elem, $($rest)*);
}; };
// Expression // Expression
($elem:expr, {$ielem:expr} $($rest:tt)*) => { ($elem:expr, {$ielem:expr} $($rest:tt)*) => {
$elem.children.push($ielem); if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
if let $crate::Children::Children(children, _) = &mut $elem.children {
children.push({$ielem});
}
osui::parse_rsx_param!($elem, $($rest)*); osui::parse_rsx_param!($elem, $($rest)*);
}; };
// Element // Element
($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => { ($elem:expr, $pelem:path { $($inner:tt)* } $($rest:tt)*) => {
$elem.children.push(osui::rsx_elem!($pelem { $($inner)* })); if $elem.children.is_none() {$elem.children = $crate::Children::Children(Vec::new(), 0)}
if let $crate::Children::Children(children, _) = &mut $elem.children {
children.push(osui::rsx_elem!($pelem { $($inner)* }));
}
osui::parse_rsx_param!($elem, $($rest)*) osui::parse_rsx_param!($elem, $($rest)*)
}; };
// Text // Text
($elem:expr, $text:expr) => { ($elem:expr, $text:expr) => {
$elem.text = format!($text); if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text))}
}; };
($elem:expr, $text:expr, $($inner:tt)*) => { ($elem:expr, $text:expr, $($inner:tt)*) => {
$elem.text = format!($text, $($inner)*); if $elem.children.is_none() {$elem.children = $crate::Children::Text(format!($text, $($inner)*))}
}; };
// Empty // Empty
@@ -309,7 +141,7 @@ macro_rules! rsx_elem_raw {
#[macro_export] #[macro_export]
macro_rules! rsx { macro_rules! rsx {
($($inner:tt)*) => {{ ($($inner:tt)*) => {{
$crate::rsx_elem_raw!( $crate::ui::div { $($inner)* } ) $crate::rsx_elem!( $crate::ui::div { $($inner)* } )
}}; }};
} }
@@ -335,21 +167,6 @@ macro_rules! css {
}}; }};
} }
/// Macro for defining a `Arc<Mutex< T >>`
#[macro_export]
macro_rules! mux {
($a:expr) => {
std::sync::Mutex::new($a)
};
}
#[macro_export]
macro_rules! arc {
($a:expr) => {
std::sync::Arc::new(std::sync::Mutex::new($a))
};
}
/// Macro for defining a `Value< T >` /// Macro for defining a `Value< T >`
#[macro_export] #[macro_export]
macro_rules! val { macro_rules! val {
+8 -2
View File
@@ -1,5 +1,11 @@
use osui::{app::run, rsx_elem, ui::text}; use osui::{app::run, rsx, ui::*, Element};
fn main() { fn main() {
run(&mut rsx_elem! { text { "Hello, World!" } }); while run(&mut app()) {}
}
fn app() -> Box<dyn Element> {
rsx! {
text { "Hello, World!" }
}
} }
-9
View File
@@ -1,9 +0,0 @@
use crate::{element, State};
element! {
Text {}
defaults {}
fn render(&self, _state: State) -> String {
self.text.clone()
}
}
+84 -5
View File
@@ -1,13 +1,92 @@
/// The `ui` module provides user interface components and utilities for building /// The `ui` module provides user interface components and utilities for building
/// a text-based user interface (TUI). It includes predefined styles and elements /// a text-based user interface (TUI). It includes predefined styles and elements
/// such as text, buttons, and containers. /// such as text, buttons, and containers.
pub mod styles; pub mod styles;
pub use styles::*; pub use styles::*;
pub mod elements;
pub use elements::*;
use crate::{Children, Element, ElementComponent, Value};
use osui_element::{elem_fn, element};
pub fn text<'a>() -> Box<Text<'a>> { #[element]
Box::new(Text::new()) #[elem_fn]
#[derive(Default, Debug)]
pub struct Text {}
impl Element for Text<'_> {
fn render(&self, _: crate::State) -> String {
if let Children::Text(text) = &self.children {
text.clone()
} else {
String::new()
}
}
}
#[element]
#[elem_fn]
#[derive(Default, Debug)]
pub struct Div {}
impl Element for Div<'_> {
fn render(&self, state: crate::State) -> String {
let mut frame = crate::utils::create_frame(self.width.get_value(), self.height.get_value());
if let Children::Children(children, child) = &self.children {
for (i, elem) in children.iter().enumerate() {
if i == *child {
crate::utils::render_to_frame(state, self.width.get_value(), &mut frame, elem);
} else {
crate::utils::render_to_frame(
crate::State::Normal,
self.width.get_value(),
&mut frame,
elem,
);
}
}
}
frame.join("\n")
}
fn event(&mut self, event: crossterm::event::Event, state: &crate::StateChanger) {
match event {
crossterm::event::Event::Key(key) => {
if let Children::Children(children, child) = &mut self.children {
*child = match key.code {
crossterm::event::KeyCode::Up => crate::utils::closest_component(
children,
*child,
crate::utils::Direction::Up,
),
crossterm::event::KeyCode::Down => crate::utils::closest_component(
children,
*child,
crate::utils::Direction::Down,
),
crossterm::event::KeyCode::Left => crate::utils::closest_component(
children,
*child,
crate::utils::Direction::Left,
),
crossterm::event::KeyCode::Right => crate::utils::closest_component(
children,
*child,
crate::utils::Direction::Right,
),
_ => *child,
}
}
}
_ => {
if let Some(child) = self.get_child() {
child.event(event, state);
}
}
}
}
} }
+9 -16
View File
@@ -78,7 +78,7 @@ pub fn render_to_frame(
state: crate::State, state: crate::State,
width: usize, width: usize,
frame: &mut Vec<String>, frame: &mut Vec<String>,
element: &mut Box<dyn Element>, element: &Box<dyn Element>,
) { ) {
let data = element.get_data(); let data = element.get_data();
for (i, line) in element.render(state).split('\n').enumerate() { for (i, line) in element.render(state).split('\n').enumerate() {
@@ -130,7 +130,7 @@ pub enum Direction {
} }
pub fn closest_component( pub fn closest_component(
components: &[&mut dyn Element], components: &[Box<dyn Element>],
current_index: usize, current_index: usize,
direction: Direction, direction: Direction,
) -> usize { ) -> usize {
@@ -144,39 +144,32 @@ pub fn closest_component(
match direction { match direction {
Direction::Left => match comp.0 { Direction::Left => match comp.0 {
Value::Custom(_) => { Value::Custom(_) => {
comp.0.get_value() < current.0.get_value() comp.0.get_value() < current.0.get_value() && comp.1 == current.1
&& comp.1 == current.1
} }
Value::Default(_) => *i < current_index, Value::Default(_) => *i < current_index,
}, // Left }, // Left
Direction::Right => match comp.0 { Direction::Right => match comp.0 {
Value::Custom(_) => { Value::Custom(_) => {
comp.0.get_value() > current.0.get_value() comp.0.get_value() > current.0.get_value() && comp.1 == current.1
&& comp.1 == current.1
} }
Value::Default(_) => *i > current_index, Value::Default(_) => *i > current_index,
}, // Right }, // Right
Direction::Up => { Direction::Up => comp.1 < current.1 && comp.0.get_value() == current.0.get_value(), // Up
comp.1 < current.1
&& comp.0.get_value() == current.0.get_value()
} // Up
Direction::Down => { Direction::Down => {
comp.1 > current.1 comp.1 > current.1 && comp.0.get_value() == current.0.get_value()
&& comp.0.get_value() == current.0.get_value()
} // Down } // Down
} }
}) })
.min_by_key(|(_, comp_)| { .min_by_key(|(_, comp_)| {
let comp = comp_.get_data(); let comp = comp_.get_data();
current.0.get_value().abs_diff(comp.0.get_value()) current.0.get_value().abs_diff(comp.0.get_value()) + current.1.abs_diff(comp.1)
+ current.1.abs_diff(comp.1)
}) // Find the closest component }) // Find the closest component
.map(|(index, _)| index) // Return the index of 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 .unwrap_or(current_index) // If no component is found, return the current index
} }
pub fn create_frame(width: Value<usize>, height: Value<usize>) -> Vec<String> { pub fn create_frame(width: usize, height: usize) -> Vec<String> {
vec![" ".repeat(width.get_value()); height.get_value()] vec![" ".repeat(width); height]
} }
pub fn get_term_size() -> (usize, usize) { pub fn get_term_size() -> (usize, usize) {