Skip to content

Commit

Permalink
Update keybinds in help window. (#41)
Browse files Browse the repository at this point in the history
* Insert mode no longer accepts empty messages

* Stored keybinds in static vectors

* vector_column_max can now take any width 2D vector

* Added more keys to the README.md tables.

Also, added insert mode to the key.rs file because I forgot about it.

* Appease Clippy
  • Loading branch information
Xithrius authored Oct 17, 2021
1 parent da2b111 commit 5404aea
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 51 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ serde = { version = "1.0.130", features = ["derive"] }
textwrap = "0.14.2"
dirs = "4.0.0"
rustyline = "9.0.0"
lazy_static = "1.4.0"

[[bin]]
bench = false
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

| Key | Description |
|-------|------------------------------------------------------------------------------------------------------|
| `?` | Have the keybinds window appear. |
| `i` | Enter insert mode for sending messages. Exit this mode with `Esc`. |
| `Esc` | Exits out of layered windows, such as going from insert mode, to normal, to exiting the application. |
| `c` | Go to the chat window chat. |
| `i` | Enter insert mode for sending messages. Exit this mode with `Esc`. |
| `?` | Have the keybinds window appear. |
| `q` | Quit out of the entire application. |
| `Esc` | Exits out of layered windows, such as going from insert mode, to normal, to exiting the application. |


</details>
Expand All @@ -26,6 +26,19 @@
|------------|-------------------------------------------------------------|
| `Ctrl + w` | Cuts a single word (from the cursor to the next whitespace) |
| `Ctrl + u` | Cuts the entire line |
| `Ctrl + f` | Move cursor to the right |
| `Ctrl + b` | Move cursor to the left |
| `Ctrl + a` | Move cursor to the start |
| `Ctrl + e` | Move cursor to the end |
| `Alt + f` | Move to the end of the next word |
| `Alt + b` | Move to the start of the previous word |
| `Ctrl + t` | Swap previous item with current item |
| `Alt + t` | Swap previous word with current word |
| `Ctrl + u` | Remove everything before the cursor |
| `Ctrl + k` | Remove everything after the cursor |
| `Ctrl + w` | Remove the previous word |
| `Ctrl + d` | Remove item to the right |
| `Esc` | Drop back to previous window layer |

</details>

Expand Down
15 changes: 9 additions & 6 deletions src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,16 @@ pub async fn ui_driver(
}
Key::Enter => {
let input_message = app.input_text.as_str();
app.messages.push_front(data_builder.user(
config.twitch.username.to_string(),
input_message.to_string(),
));

tx.send(input_message.to_string()).await.unwrap();
app.input_text.update("", 0);
if !input_message.is_empty() {
app.messages.push_front(data_builder.user(
config.twitch.username.to_string(),
input_message.to_string(),
));

tx.send(input_message.to_string()).await.unwrap();
app.input_text.update("", 0);
}
}
Key::Char(c) => {
app.input_text.insert(c, 1);
Expand Down
66 changes: 39 additions & 27 deletions src/ui/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use tui::{
widgets::{Block, Borders, Row, Table},
};

use crate::utils::{styles, text::vector2_col_max};
use crate::{
ui::keys::{COLUMN_TITLES, INSERT_MODE, NORMAL_MODE},
utils::{styles, text::vector_column_max},
};

pub fn draw_keybinds_ui<T>(frame: &mut Frame<T>) -> Result<()>
where
Expand All @@ -15,37 +18,46 @@ where
let vertical_chunks = Layout::default()
.direction(Direction::Vertical)
.margin(5)
.constraints([Constraint::Percentage(100)].as_ref())
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(frame.size());

let mut keybinds = vec![
vec!["Description", "Keybind"],
vec!["Bring up the chat window", "c"],
vec!["Keybinds help (this window)", "?"],
vec![
"Exit out layer window/entire app when in normal mode",
"Esc",
],
vec!["Quit this application", "q"],
];

let (maximum_description_width, maximum_keybind_width) = vector2_col_max(&keybinds);

let column_names = keybinds.remove(0);

let table_widths = vec![
Constraint::Min(maximum_description_width),
Constraint::Min(maximum_keybind_width),
];

let table = Table::new(keybinds.iter().map(|k| Row::new(k.iter().copied())))
.header(Row::new(column_names.iter().copied()).style(styles::COLUMN_TITLE))
.block(Block::default().borders(Borders::ALL).title("[ Keybinds ]"))
.widths(&table_widths)
// Normal mode keybinds
let normal_table_widths = vector_column_max(&INSERT_MODE, None)
.into_iter()
.map(Constraint::Min)
.collect::<Vec<Constraint>>();

let normal_mode_table = Table::new(NORMAL_MODE.iter().map(|k| Row::new(k.iter().copied())))
.header(Row::new(COLUMN_TITLES.iter().copied()).style(styles::COLUMN_TITLE))
.block(
Block::default()
.borders(Borders::ALL)
.title("[ Normal Mode Keybinds ]"),
)
.widths(&normal_table_widths)
.column_spacing(2)
.style(styles::BORDER_NAME);

frame.render_widget(normal_mode_table, vertical_chunks[0]);

// Insert mode keybinds
let insert_table_widths = vector_column_max(&INSERT_MODE, None)
.into_iter()
.map(Constraint::Min)
.collect::<Vec<Constraint>>();

let insert_mode_table = Table::new(INSERT_MODE.iter().map(|k| Row::new(k.iter().copied())))
.header(Row::new(COLUMN_TITLES.iter().copied()).style(styles::COLUMN_TITLE))
.block(
Block::default()
.borders(Borders::ALL)
.title("[ Insert Mode Keybinds ]"),
)
.widths(&insert_table_widths)
.column_spacing(2)
.style(styles::BORDER_NAME);

frame.render_widget(table, vertical_chunks[0]);
frame.render_widget(insert_mode_table, vertical_chunks[1]);

Ok(())
}
27 changes: 27 additions & 0 deletions src/ui/keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use lazy_static::lazy_static;

lazy_static! {
pub static ref COLUMN_TITLES: Vec<&'static str> = vec!["Keybind", "Description"];
pub static ref NORMAL_MODE: Vec<Vec<&'static str>> = vec![
vec!["c", "Chat window"],
vec!["i", "Insert mode"],
vec!["?", "Bring up this window"],
vec!["q", "Quit this application"],
vec!["Esc", "Drop back to previous window layer"],
];
pub static ref INSERT_MODE: Vec<Vec<&'static str>> = vec![
vec!["Ctrl + f", "Move cursor to the right"],
vec!["Ctrl + b", "Move cursor to the left"],
vec!["Ctrl + a", "Move cursor to the start"],
vec!["Ctrl + e", "Move cursor to the end"],
vec!["Alt + f", "Move to the end of the next word"],
vec!["Alt + b", "Move to the start of the previous word"],
vec!["Ctrl + t", "Swap previous item with current item"],
vec!["Alt + t", "Swap previous word with current word"],
vec!["Ctrl + u", "Remove everything before the cursor"],
vec!["Ctrl + k", "Remove everything after the cursor"],
vec!["Ctrl + w", "Remove the previous word"],
vec!["Ctrl + d", "Remove item to the right"],
vec!["Esc", "Drop back to previous window layer"],
];
}
1 change: 1 addition & 0 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod chat;
pub mod help;
pub mod keys;
83 changes: 68 additions & 15 deletions src/utils/text.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::vec::IntoIter;

use rustyline::line_buffer::LineBuffer;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
Expand Down Expand Up @@ -25,14 +27,49 @@ pub fn align_text(text: &str, alignment: &str, maximum_length: u16) -> String {
}
}

pub fn vector2_col_max<T>(vec2: &[Vec<T>]) -> (u16, u16)
pub enum VectorColumnMax<T> {
One(T),
All(Vec<T>),
}

impl<T> IntoIterator for VectorColumnMax<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<Self::Item>;

fn into_iter(self) -> Self::IntoIter {
match self {
VectorColumnMax::All(item) => item.into_iter(),
VectorColumnMax::One(item) => vec![item].into_iter(),
}
}
}

pub fn vector_column_max<T>(vec: &[Vec<T>], indexer: Option<usize>) -> IntoIter<u16>
where
T: AsRef<str>,
{
let col0 = vec2.iter().map(|v| v[0].as_ref().len()).max().unwrap();
let col1 = vec2.iter().map(|v| v[1].as_ref().len()).max().unwrap();
if vec.is_empty() {
panic!("Vector length should be greater than or equal to 1.")
}

let column_max = |vec: &[Vec<T>], index: usize| -> u16 {
vec.iter().map(|v| v[index].as_ref().len()).max().unwrap() as u16
};

match indexer {
Some(index) => VectorColumnMax::One(column_max(vec, index)).into_iter(),
None => {
let column_amount = vec[0].len();

let mut column_max_lengths: Vec<u16> = vec![];

(col0 as u16, col1 as u16)
for i in 0..column_amount {
column_max_lengths.push(column_max(vec, i));
}

VectorColumnMax::All(column_max_lengths).into_iter()
}
}
}

pub fn get_cursor_position(line_buffer: &LineBuffer) -> usize {
Expand All @@ -50,7 +87,7 @@ mod tests {

#[test]
#[should_panic(expected = "Parameter of 'maximum_length' cannot be below 1.")]
fn test_maximum_length() {
fn test_text_align_maximum_length() {
align_text("", "left", 0);
}

Expand Down Expand Up @@ -79,26 +116,42 @@ mod tests {
}

#[test]
fn test_reference_string_vec2() {
let vec2 = vec![vec!["", "s"], vec!["longer string", "lll"]];
#[should_panic(expected = "Vector length should be greater than or equal to 1.")]
fn test_vector_column_max_empty_vector() {
let vec: Vec<Vec<String>> = vec![];

let (col0, col1) = vector2_col_max(&vec2);
vector_column_max(&vec, None);
}

#[test]
fn test_vector_column_max_reference_strings() {
let vec = vec![vec!["", "s"], vec!["longer string", "lll"]];

assert_eq!(col0, 13);
assert_eq!(col1, 3);
let mut output_vec_all = vector_column_max(&vec, None);

assert_eq!(output_vec_all.next(), Some(13));
assert_eq!(output_vec_all.next(), Some(3));

let mut output_vec_one = vector_column_max(&vec, Some(0));

assert_eq!(output_vec_one.next(), Some(13));
}

#[test]
fn test_string_vec2() {
let vec2 = vec![
fn test_vector_column_max_strings() {
let vec = vec![
vec!["".to_string(), "another".to_string()],
vec!["".to_string(), "the last string".to_string()],
];

let (col0, col1) = vector2_col_max(&vec2);
let mut output_vec_all = vector_column_max(&vec, None);

assert_eq!(output_vec_all.next(), Some(0));
assert_eq!(output_vec_all.next(), Some(15));

let mut output_vec_one = vector_column_max(&vec, Some(0));

assert_eq!(col0, 0);
assert_eq!(col1, 15);
assert_eq!(output_vec_one.next(), Some(0));
}

#[test]
Expand Down

0 comments on commit 5404aea

Please sign in to comment.