diff --git a/src/app.rs b/src/app.rs
index abfc7b1..55431af 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -172,10 +172,10 @@ impl App {
&database,
&table,
0,
- if self.record_table.filter.input_str().is_empty() {
+ if self.record_table.filter.input.value_str().is_empty() {
None
} else {
- Some(self.record_table.filter.input_str())
+ Some(self.record_table.filter.input.value_str())
},
)
.await?;
@@ -278,10 +278,11 @@ impl App {
&database,
&table,
index as u16,
- if self.record_table.filter.input_str().is_empty() {
+ if self.record_table.filter.input.value_str().is_empty()
+ {
None
} else {
- Some(self.record_table.filter.input_str())
+ Some(self.record_table.filter.input.value_str())
},
)
.await?;
diff --git a/src/components/database_filter.rs b/src/components/database_filter.rs
index 6aeea78..3106859 100644
--- a/src/components/database_filter.rs
+++ b/src/components/database_filter.rs
@@ -1,5 +1,6 @@
-use super::{compute_character_width, Component, DrawableComponent, EventState};
+use super::{Component, DrawableComponent, EventState};
use crate::components::command::CommandInfo;
+use crate::components::utils::input::Input;
use crate::event::Key;
use anyhow::Result;
use database_tree::Table;
@@ -11,34 +12,23 @@ use tui::{
widgets::{Block, Borders, Paragraph},
Frame,
};
-use unicode_width::UnicodeWidthStr;
pub struct DatabaseFilterComponent {
pub table: Option
,
- input: Vec,
- input_idx: usize,
- input_cursor_position: u16,
+ pub input: Input,
}
impl DatabaseFilterComponent {
pub fn new() -> Self {
Self {
table: None,
- input: Vec::new(),
- input_idx: 0,
- input_cursor_position: 0,
+ input: Input::new(),
}
}
- pub fn input_str(&self) -> String {
- self.input.iter().collect()
- }
-
pub fn reset(&mut self) {
self.table = None;
- self.input = Vec::new();
- self.input_idx = 0;
- self.input_cursor_position = 0;
+ self.input.reset();
}
}
@@ -46,10 +36,10 @@ impl DrawableComponent for DatabaseFilterComponent {
fn draw(&self, f: &mut Frame, area: Rect, focused: bool) -> Result<()> {
let query = Paragraph::new(Spans::from(format!(
"{:w$}",
- if self.input.is_empty() && !focused {
+ if self.input.value.is_empty() && !focused {
"Filter tables".to_string()
} else {
- self.input_str()
+ self.input.value_str()
},
w = area.width as usize
)))
@@ -63,7 +53,7 @@ impl DrawableComponent for DatabaseFilterComponent {
if focused {
f.set_cursor(
- (area.x + self.input_cursor_position).min(area.right().saturating_sub(1)),
+ (area.x + self.input.cursor_position).min(area.right().saturating_sub(1)),
area.y,
)
}
@@ -75,58 +65,9 @@ impl Component for DatabaseFilterComponent {
fn commands(&self, _out: &mut Vec) {}
fn event(&mut self, key: Key) -> Result {
- let input_str: String = self.input.iter().collect();
-
- match key {
- Key::Char(c) => {
- self.input.insert(self.input_idx, c);
- self.input_idx += 1;
- self.input_cursor_position += compute_character_width(c);
-
- return Ok(EventState::Consumed);
- }
- Key::Delete | Key::Backspace => {
- if input_str.width() > 0 && !self.input.is_empty() && self.input_idx > 0 {
- let last_c = self.input.remove(self.input_idx - 1);
- self.input_idx -= 1;
- self.input_cursor_position -= compute_character_width(last_c);
- }
- return Ok(EventState::Consumed);
- }
- Key::Left => {
- if !self.input.is_empty() && self.input_idx > 0 {
- self.input_idx -= 1;
- self.input_cursor_position = self
- .input_cursor_position
- .saturating_sub(compute_character_width(self.input[self.input_idx]));
- }
- return Ok(EventState::Consumed);
- }
- Key::Ctrl('a') => {
- if !self.input.is_empty() && self.input_idx > 0 {
- self.input_idx = 0;
- self.input_cursor_position = 0
- }
- return Ok(EventState::Consumed);
- }
- Key::Right => {
- if self.input_idx < self.input.len() {
- let next_c = self.input[self.input_idx];
- self.input_idx += 1;
- self.input_cursor_position += compute_character_width(next_c);
- }
- return Ok(EventState::Consumed);
- }
- Key::Ctrl('e') => {
- if self.input_idx < self.input.len() {
- self.input_idx = self.input.len();
- self.input_cursor_position = self.input_str().width() as u16;
- }
- return Ok(EventState::Consumed);
- }
- _ => (),
+ match self.input.handle_key(key) {
+ (Some(_), _) => Ok(EventState::Consumed),
+ _ => Ok(EventState::NotConsumed),
}
-
- Ok(EventState::NotConsumed)
}
}
diff --git a/src/components/databases.rs b/src/components/databases.rs
index e4af306..218ec48 100644
--- a/src/components/databases.rs
+++ b/src/components/databases.rs
@@ -190,10 +190,10 @@ impl DatabasesComponent {
item.clone(),
selected,
area.width,
- if self.filter.input_str().is_empty() {
+ if self.filter.input.value_str().is_empty() {
None
} else {
- Some(self.filter.input_str())
+ Some(self.filter.input.value_str())
},
)
});
@@ -229,10 +229,10 @@ impl Component for DatabasesComponent {
}
if matches!(self.focus, Focus::Filter) {
- self.filterd_tree = if self.filter.input_str().is_empty() {
+ self.filterd_tree = if self.filter.input.value_str().is_empty() {
None
} else {
- Some(self.tree.filter(self.filter.input_str()))
+ Some(self.tree.filter(self.filter.input.value_str()))
};
}
diff --git a/src/components/sql_editor.rs b/src/components/sql_editor.rs
index 50e53cf..c1423d4 100644
--- a/src/components/sql_editor.rs
+++ b/src/components/sql_editor.rs
@@ -3,6 +3,7 @@ use super::{
StatefulDrawableComponent, TableComponent,
};
use crate::components::command::CommandInfo;
+use crate::components::utils::input::Input;
use crate::config::KeyConfig;
use crate::database::{ExecuteResult, Pool};
use crate::event::Key;
@@ -34,9 +35,7 @@ pub enum Focus {
}
pub struct SqlEditorComponent {
- input: Vec,
- input_cursor_position_x: u16,
- input_idx: usize,
+ input: Input,
table: TableComponent,
query_result: Option,
completion: CompletionComponent,
@@ -48,9 +47,7 @@ pub struct SqlEditorComponent {
impl SqlEditorComponent {
pub fn new(key_config: KeyConfig) -> Self {
Self {
- input: Vec::new(),
- input_idx: 0,
- input_cursor_position_x: 0,
+ input: Input::new(),
table: TableComponent::new(key_config.clone()),
completion: CompletionComponent::new(key_config.clone(), "", true),
focus: Focus::Editor,
@@ -63,9 +60,10 @@ impl SqlEditorComponent {
fn update_completion(&mut self) {
let input = &self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i < &self.input_idx)
+ .filter(|(i, _)| i < &self.input.cursor_index)
.map(|(_, i)| i)
.collect::()
.split(' ')
@@ -80,16 +78,23 @@ impl SqlEditorComponent {
let mut input = Vec::new();
let first = self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i < &self.input_idx.saturating_sub(self.completion.word().len()))
+ .filter(|(i, _)| {
+ i < &self
+ .input
+ .cursor_index
+ .saturating_sub(self.completion.word().len())
+ })
.map(|(_, c)| c.to_string())
.collect::>();
let last = self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i >= &self.input_idx)
+ .filter(|(i, _)| i >= &self.input.cursor_index)
.map(|(_, c)| c.to_string())
.collect::>();
@@ -113,21 +118,21 @@ impl SqlEditorComponent {
input.extend(middle.clone());
input.extend(last);
- self.input = input.join("").chars().collect();
- self.input_idx += &middle.len();
+ self.input.value = input.join("").chars().collect();
+ self.input.cursor_index += &middle.len();
if is_last_word {
- self.input_idx += 1;
+ self.input.cursor_index += 1;
}
- self.input_idx -= self.completion.word().len();
- self.input_cursor_position_x += middle
+ self.input.cursor_index -= self.completion.word().len();
+ self.input.cursor_position += middle
.join("")
.chars()
.map(compute_character_width)
.sum::();
if is_last_word {
- self.input_cursor_position_x += " ".to_string().width() as u16
+ self.input.cursor_position += " ".to_string().width() as u16
}
- self.input_cursor_position_x -= self
+ self.input.cursor_position -= self
.completion
.word()
.chars()
@@ -151,7 +156,7 @@ impl StatefulDrawableComponent for SqlEditorComponent {
})
.split(area);
- let editor = StatefulParagraph::new(self.input.iter().collect::())
+ let editor = StatefulParagraph::new(self.input.value.iter().collect::())
.wrap(Wrap { trim: true })
.block(Block::default().borders(Borders::ALL));
@@ -176,14 +181,10 @@ impl StatefulDrawableComponent for SqlEditorComponent {
if focused && matches!(self.focus, Focus::Editor) {
f.set_cursor(
(layout[0].x + 1)
- .saturating_add(
- self.input_cursor_position_x % layout[0].width.saturating_sub(2),
- )
+ .saturating_add(self.input.cursor_position % layout[0].width.saturating_sub(2))
.min(area.right().saturating_sub(2)),
- (layout[0].y
- + 1
- + self.input_cursor_position_x / layout[0].width.saturating_sub(2))
- .min(layout[0].bottom()),
+ (layout[0].y + 1 + self.input.cursor_position / layout[0].width.saturating_sub(2))
+ .min(layout[0].bottom()),
)
}
@@ -192,8 +193,8 @@ impl StatefulDrawableComponent for SqlEditorComponent {
f,
area,
false,
- self.input_cursor_position_x % layout[0].width.saturating_sub(2) + 1,
- self.input_cursor_position_x / layout[0].width.saturating_sub(2),
+ self.input.cursor_position % layout[0].width.saturating_sub(2) + 1,
+ self.input.cursor_position / layout[0].width.saturating_sub(2),
)?;
};
Ok(())
@@ -205,62 +206,48 @@ impl Component for SqlEditorComponent {
fn commands(&self, _out: &mut Vec) {}
fn event(&mut self, key: Key) -> Result {
- let input_str: String = self.input.iter().collect();
-
if key == self.key_config.focus_above && matches!(self.focus, Focus::Table) {
self.focus = Focus::Editor
} else if key == self.key_config.enter {
return self.complete();
}
- match key {
- Key::Char(c) if matches!(self.focus, Focus::Editor) => {
- self.input.insert(self.input_idx, c);
- self.input_idx += 1;
- self.input_cursor_position_x += compute_character_width(c);
- self.update_completion();
+ if matches!(self.focus, Focus::Table) {
+ return self.table.event(key);
+ }
- return Ok(EventState::Consumed);
- }
- Key::Esc if matches!(self.focus, Focus::Editor) => self.focus = Focus::Table,
- Key::Delete | Key::Backspace if matches!(self.focus, Focus::Editor) => {
- if input_str.width() > 0 && !self.input.is_empty() && self.input_idx > 0 {
- let last_c = self.input.remove(self.input_idx - 1);
- self.input_idx -= 1;
- self.input_cursor_position_x -= compute_character_width(last_c);
- self.completion.update("");
- }
+ if !matches!(self.focus, Focus::Editor) {
+ return Ok(EventState::NotConsumed);
+ }
- return Ok(EventState::Consumed);
- }
- Key::Left if matches!(self.focus, Focus::Editor) => {
- if !self.input.is_empty() && self.input_idx > 0 {
- self.input_idx -= 1;
- self.input_cursor_position_x = self
- .input_cursor_position_x
- .saturating_sub(compute_character_width(self.input[self.input_idx]));
- self.completion.update("");
- }
- return Ok(EventState::Consumed);
- }
- Key::Right if matches!(self.focus, Focus::Editor) => {
- if self.input_idx < self.input.len() {
- let next_c = self.input[self.input_idx];
- self.input_idx += 1;
- self.input_cursor_position_x += compute_character_width(next_c);
- self.completion.update("");
- }
- return Ok(EventState::Consumed);
+ if key == Key::Esc {
+ self.focus = Focus::Table;
+ return Ok(EventState::Consumed);
+ } else {
+ match self.input.handle_key(key) {
+ (Some(matched_key), input_updated) => match matched_key {
+ Key::Char(_) => {
+ self.update_completion();
+ return Ok(EventState::Consumed);
+ }
+ Key::Ctrl(_) => {
+ return Ok(EventState::Consumed);
+ }
+ _ => {
+ if input_updated {
+ self.completion.update("");
+ }
+ return Ok(EventState::Consumed);
+ }
+ },
+ _ => return Ok(EventState::NotConsumed),
}
- key if matches!(self.focus, Focus::Table) => return self.table.event(key),
- _ => (),
}
- return Ok(EventState::NotConsumed);
}
async fn async_event(&mut self, key: Key, pool: &Box) -> Result {
if key == self.key_config.enter && matches!(self.focus, Focus::Editor) {
- let query = self.input.iter().collect();
+ let query = self.input.value.iter().collect();
let result = pool.execute(&query).await?;
match result {
ExecuteResult::Read {
diff --git a/src/components/table_filter.rs b/src/components/table_filter.rs
index 9875f24..8f3126a 100644
--- a/src/components/table_filter.rs
+++ b/src/components/table_filter.rs
@@ -3,6 +3,7 @@ use super::{
StatefulDrawableComponent,
};
use crate::components::command::CommandInfo;
+use crate::components::utils::input::Input;
use crate::config::KeyConfig;
use crate::event::Key;
use anyhow::Result;
@@ -20,9 +21,7 @@ use unicode_width::UnicodeWidthStr;
pub struct TableFilterComponent {
key_config: KeyConfig,
pub table: Option,
- input: Vec,
- input_idx: usize,
- input_cursor_position: u16,
+ pub input: Input,
completion: CompletionComponent,
}
@@ -31,30 +30,23 @@ impl TableFilterComponent {
Self {
key_config: key_config.clone(),
table: None,
- input: Vec::new(),
- input_idx: 0,
- input_cursor_position: 0,
+ input: Input::new(),
completion: CompletionComponent::new(key_config, "", false),
}
}
- pub fn input_str(&self) -> String {
- self.input.iter().collect()
- }
-
pub fn reset(&mut self) {
+ self.input.reset();
self.table = None;
- self.input = Vec::new();
- self.input_idx = 0;
- self.input_cursor_position = 0;
}
fn update_completion(&mut self) {
let input = &self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i < &self.input_idx)
+ .filter(|(i, _)| i < &self.input.cursor_index)
.map(|(_, i)| i)
.collect::()
.split(' ')
@@ -69,16 +61,23 @@ impl TableFilterComponent {
let mut input = Vec::new();
let first = self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i < &self.input_idx.saturating_sub(self.completion.word().len()))
+ .filter(|(i, _)| {
+ i < &self
+ .input
+ .cursor_index
+ .saturating_sub(self.completion.word().len())
+ })
.map(|(_, c)| c.to_string())
.collect::>();
let last = self
.input
+ .value
.iter()
.enumerate()
- .filter(|(i, _)| i >= &self.input_idx)
+ .filter(|(i, _)| i >= &self.input.cursor_index)
.map(|(_, c)| c.to_string())
.collect::>();
@@ -102,21 +101,21 @@ impl TableFilterComponent {
input.extend(middle.clone());
input.extend(last);
- self.input = input.join("").chars().collect();
- self.input_idx += &middle.len();
+ self.input.value = input.join("").chars().collect();
+ self.input.cursor_index += &middle.len();
if is_last_word {
- self.input_idx += 1;
+ self.input.cursor_index += 1;
}
- self.input_idx -= self.completion.word().len();
- self.input_cursor_position += middle
+ self.input.cursor_index -= self.completion.word().len();
+ self.input.cursor_position += middle
.join("")
.chars()
.map(compute_character_width)
.sum::();
if is_last_word {
- self.input_cursor_position += " ".to_string().width() as u16
+ self.input.cursor_position += " ".to_string().width() as u16
}
- self.input_cursor_position -= self
+ self.input.cursor_position -= self
.completion
.word()
.chars()
@@ -140,8 +139,8 @@ impl StatefulDrawableComponent for TableFilterComponent {
),
Span::from(format!(
" {}",
- if focused || !self.input.is_empty() {
- self.input.iter().collect::()
+ if focused || !self.input.value.is_empty() {
+ self.input.value.iter().collect::()
} else {
"Enter a SQL expression in WHERE clause to filter records".to_string()
}
@@ -167,7 +166,7 @@ impl StatefulDrawableComponent for TableFilterComponent {
format!("{} ", table.name.to_string())
})
.width() as u16)
- .saturating_add(self.input_cursor_position),
+ .saturating_add(self.input.cursor_position),
0,
)?;
};
@@ -181,7 +180,7 @@ impl StatefulDrawableComponent for TableFilterComponent {
.map_or(String::new(), |table| table.name.to_string())
.width()
+ 1) as u16)
- .saturating_add(self.input_cursor_position)
+ .saturating_add(self.input.cursor_position)
.min(area.right().saturating_sub(2)),
area.y + 1,
)
@@ -194,8 +193,6 @@ impl Component for TableFilterComponent {
fn commands(&self, _out: &mut Vec) {}
fn event(&mut self, key: Key) -> Result {
- let input_str: String = self.input.iter().collect();
-
// apply comletion candidates
if key == self.key_config.enter {
return self.complete();
@@ -203,58 +200,23 @@ impl Component for TableFilterComponent {
self.completion.selected_candidate();
- match key {
- Key::Char(c) => {
- self.input.insert(self.input_idx, c);
- self.input_idx += 1;
- self.input_cursor_position += compute_character_width(c);
- self.update_completion();
-
- Ok(EventState::Consumed)
- }
- Key::Delete | Key::Backspace => {
- if input_str.width() > 0 && !self.input.is_empty() && self.input_idx > 0 {
- let last_c = self.input.remove(self.input_idx - 1);
- self.input_idx -= 1;
- self.input_cursor_position -= compute_character_width(last_c);
- self.completion.update("");
- }
- Ok(EventState::Consumed)
- }
- Key::Left => {
- if !self.input.is_empty() && self.input_idx > 0 {
- self.input_idx -= 1;
- self.input_cursor_position = self
- .input_cursor_position
- .saturating_sub(compute_character_width(self.input[self.input_idx]));
- self.completion.update("");
+ match self.input.handle_key(key) {
+ (Some(matched_key), input_updated) => match matched_key {
+ Key::Char(_) => {
+ self.update_completion();
+ return Ok(EventState::Consumed);
}
- Ok(EventState::Consumed)
- }
- Key::Ctrl('a') => {
- if !self.input.is_empty() && self.input_idx > 0 {
- self.input_idx = 0;
- self.input_cursor_position = 0
- }
- Ok(EventState::Consumed)
- }
- Key::Right => {
- if self.input_idx < self.input.len() {
- let next_c = self.input[self.input_idx];
- self.input_idx += 1;
- self.input_cursor_position += compute_character_width(next_c);
- self.completion.update("");
+ Key::Ctrl(_) => {
+ return Ok(EventState::Consumed);
}
- Ok(EventState::Consumed)
- }
- Key::Ctrl('e') => {
- if self.input_idx < self.input.len() {
- self.input_idx = self.input.len();
- self.input_cursor_position = self.input_str().width() as u16;
+ _ => {
+ if input_updated {
+ self.completion.update("");
+ }
+ return Ok(EventState::Consumed);
}
- Ok(EventState::Consumed)
- }
- key => self.completion.event(key),
+ },
+ _ => self.completion.event(key),
}
}
}
@@ -266,12 +228,12 @@ mod test {
#[test]
fn test_complete() {
let mut filter = TableFilterComponent::new(KeyConfig::default());
- filter.input_idx = 2;
- filter.input = vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g'];
+ filter.input.cursor_index = 2;
+ filter.input.value = vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g'];
filter.completion.update("an");
assert!(filter.complete().is_ok());
assert_eq!(
- filter.input,
+ filter.input.value,
vec!['A', 'N', 'D', ' ', 'c', 'd', 'e', 'f', 'g']
);
}
@@ -279,12 +241,12 @@ mod test {
#[test]
fn test_complete_end() {
let mut filter = TableFilterComponent::new(KeyConfig::default());
- filter.input_idx = 9;
- filter.input = vec!['a', 'b', ' ', 'c', 'd', 'e', 'f', ' ', 'i'];
+ filter.input.cursor_index = 9;
+ filter.input.value = vec!['a', 'b', ' ', 'c', 'd', 'e', 'f', ' ', 'i'];
filter.completion.update('i');
assert!(filter.complete().is_ok());
assert_eq!(
- filter.input,
+ filter.input.value,
vec!['a', 'b', ' ', 'c', 'd', 'e', 'f', ' ', 'I', 'N', ' ']
);
}
@@ -292,10 +254,13 @@ mod test {
#[test]
fn test_complete_no_candidates() {
let mut filter = TableFilterComponent::new(KeyConfig::default());
- filter.input_idx = 2;
- filter.input = vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g'];
+ filter.input.cursor_index = 2;
+ filter.input.value = vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g'];
filter.completion.update("foo");
assert!(filter.complete().is_ok());
- assert_eq!(filter.input, vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g']);
+ assert_eq!(
+ filter.input.value,
+ vec!['a', 'n', ' ', 'c', 'd', 'e', 'f', 'g']
+ );
}
}
diff --git a/src/components/utils/input.rs b/src/components/utils/input.rs
new file mode 100644
index 0000000..d03af21
--- /dev/null
+++ b/src/components/utils/input.rs
@@ -0,0 +1,88 @@
+use crate::components::compute_character_width;
+use crate::event::Key;
+use unicode_width::UnicodeWidthStr;
+
+pub struct Input {
+ pub value: Vec,
+ pub cursor_position: u16,
+ pub cursor_index: usize,
+}
+
+impl Input {
+ pub fn new() -> Self {
+ Self {
+ value: Vec::new(),
+ cursor_index: 0,
+ cursor_position: 0,
+ }
+ }
+
+ pub fn value_str(&self) -> String {
+ self.value.iter().collect()
+ }
+
+ pub fn reset(&mut self) {
+ self.value = Vec::new();
+ self.cursor_index = 0;
+ self.cursor_position = 0;
+ }
+
+ pub fn handle_key(&mut self, key: Key) -> (Option, bool) {
+ let value_str: String = self.value.iter().collect();
+
+ match key {
+ Key::Char(c) => {
+ self.value.insert(self.cursor_index, c);
+ self.cursor_index += 1;
+ self.cursor_position += compute_character_width(c);
+
+ return (Some(key), true);
+ }
+ Key::Delete | Key::Backspace => {
+ if value_str.width() > 0 && !self.value.is_empty() && self.cursor_index > 0 {
+ let last_c = self.value.remove(self.cursor_index - 1);
+ self.cursor_index -= 1;
+ self.cursor_position -= compute_character_width(last_c);
+ return (Some(key), true);
+ }
+ return (Some(key), false);
+ }
+ Key::Left => {
+ if !self.value.is_empty() && self.cursor_index > 0 {
+ self.cursor_index -= 1;
+ self.cursor_position = self
+ .cursor_position
+ .saturating_sub(compute_character_width(self.value[self.cursor_index]));
+ return (Some(key), true);
+ }
+ return (Some(key), false);
+ }
+ Key::Right => {
+ if self.cursor_index < self.value.len() {
+ let next_c = self.value[self.cursor_index];
+ self.cursor_index += 1;
+ self.cursor_position += compute_character_width(next_c);
+ return (Some(key), true);
+ }
+ return (Some(key), false);
+ }
+ Key::Ctrl('a') => {
+ if !self.value.is_empty() && self.cursor_index > 0 {
+ self.cursor_index = 0;
+ self.cursor_position = 0;
+ return (Some(key), true);
+ }
+ return (Some(key), false);
+ }
+ Key::Ctrl('e') => {
+ if self.cursor_index < self.value.len() {
+ self.cursor_index = self.value.len();
+ self.cursor_position = self.value_str().width() as u16;
+ return (Some(key), true);
+ }
+ return (Some(key), false);
+ }
+ _ => (None, false),
+ }
+ }
+}
diff --git a/src/components/utils/mod.rs b/src/components/utils/mod.rs
index 5860c6c..4f6d44f 100644
--- a/src/components/utils/mod.rs
+++ b/src/components/utils/mod.rs
@@ -1 +1,2 @@
+pub mod input;
pub mod scroll_vertical;