use std::{io::Result, vec};
use r3bl_ansi_color::{AnsiStyledText, Color, Style as RStyle};
use r3bl_core::{call_if_true,
get_size,
get_terminal_width,
throws,
try_initialize_global_logging};
use r3bl_tuify::{components::style::StyleSheet,
select_from_list,
select_from_list_with_multi_line_header,
SelectionMode,
DEVELOPMENT_MODE};
mod single_select_quiz_game;
use single_select_quiz_game::main as single_select_quiz_game;
fn main() -> Result<()> {
throws!({
call_if_true!(DEVELOPMENT_MODE, {
try_initialize_global_logging(tracing_core::LevelFilter::DEBUG).ok();
tracing::debug!("Start logging... terminal window size: {:?}", get_size()?)
});
let max_width_col_count: usize = get_terminal_width();
let max_height_row_count: usize = 5;
let default_style = StyleSheet::default();
let sea_foam_style = StyleSheet::sea_foam_style();
let hot_pink_style = StyleSheet::hot_pink_style();
const MULTI_LINE_HEADER: &str = "Multi line header";
const SINGLE_LINE_HEADER: &str = "Single line header";
const MULTIPLE_SELECT_SINGLE_ITEM: &str = "Multiple select, single item";
const MULTIPLE_SELECT_13_ITEMS_VPH_5: &str =
"Multiple select, 13 items, viewport height = 5";
const MULTIPLE_SELECT_2_ITEMS_VPH_5: &str =
"Multiple select, 2 items, viewport height = 5";
const SINGLE_SELECT_13_ITEMS_VPH_5: &str =
"Single select, 13 items, viewport height = 5";
const SINGLE_SELECT_2_ITEMS_VPH_5: &str =
"Single select, 2 items, viewport height = 5";
const SINGLE_SELECT_QUIZ_GAME: &str = "Single select, quiz game";
let maybe_user_input = select_from_list(
"Select which example to run".to_string(),
[
MULTI_LINE_HEADER,
SINGLE_LINE_HEADER,
MULTIPLE_SELECT_SINGLE_ITEM,
MULTIPLE_SELECT_13_ITEMS_VPH_5,
MULTIPLE_SELECT_2_ITEMS_VPH_5,
SINGLE_SELECT_13_ITEMS_VPH_5,
SINGLE_SELECT_2_ITEMS_VPH_5,
SINGLE_SELECT_QUIZ_GAME,
]
.iter()
.map(|it| it.to_string())
.collect(),
6,
0,
SelectionMode::Single,
StyleSheet::default(),
);
match &maybe_user_input {
Some(input) => {
let first_line = input.first();
match first_line {
Some(user_input) => {
if user_input == MULTI_LINE_HEADER {
multi_line_header();
} else if user_input == SINGLE_LINE_HEADER {
single_line_header();
} else if user_input == MULTIPLE_SELECT_SINGLE_ITEM {
multiple_select_single_item()
} else if user_input == MULTIPLE_SELECT_13_ITEMS_VPH_5 {
multiple_select_13_items_vph_5(
max_height_row_count,
max_width_col_count,
sea_foam_style,
);
} else if user_input == MULTIPLE_SELECT_2_ITEMS_VPH_5 {
multiple_select_2_items_vph_5(
max_height_row_count,
max_width_col_count,
sea_foam_style,
);
} else if user_input == SINGLE_SELECT_13_ITEMS_VPH_5 {
single_select_13_items_vph_5(
max_height_row_count,
max_width_col_count,
hot_pink_style,
);
} else if user_input == SINGLE_SELECT_2_ITEMS_VPH_5 {
single_select_2_items_vph_5(
max_height_row_count,
max_width_col_count,
default_style,
);
} else if user_input == SINGLE_SELECT_QUIZ_GAME {
let _ = single_select_quiz_game();
} else {
println!("User did not select anything")
}
}
None => println!("User did not select anything"),
}
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("Stop logging...");
});
});
}
fn multi_line_header() {
let header = AnsiStyledText {
text: " Please select one or more items. This is a really long heading that just keeps going and if your terminal viewport is small enough, this heading will be clipped",
style: &[
RStyle::Foreground(Color::Rgb(171, 204, 242)),
RStyle::Background(Color::Rgb(31, 36, 46)),
],
};
let line_5 = vec![header];
let mut instructions: Vec<Vec<AnsiStyledText>> = multi_select_instructions();
instructions.push(line_5);
let user_input = select_from_list_with_multi_line_header(
instructions,
[
"item 1 of 13",
"item 2 of 13",
"item 3 of 13",
"item 4 of 13",
"item 5 of 13",
"item 6 of 13",
"item 7 of 13",
"item 8 of 13",
"item 9 of 13",
"item 10 of 13",
"item 11 of 13",
"item 12 of 13",
"item 13 of 13",
]
.iter()
.map(|it| it.to_string())
.collect(),
Some(6),
None,
SelectionMode::Multiple,
StyleSheet::default(),
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
}
fn single_line_header() {
let max_width_col_count: usize = get_terminal_width();
let user_input = select_from_list(
"🦜 Please select one or more items. This is an example of a very long header text 🐧. You can pass emoji here 🐥 and text gets clipped off correctly 🐒, based on terminal size".to_string(),
[
"item 1 of 13",
"item 2 of 13",
"item 3 of 13",
"item 4 of 13",
"item 5 of 13",
"item 6 of 13",
"item 7 of 13",
"item 8 of 13",
"item 9 of 13",
"item 10 of 13",
"item 11 of 13",
"item 12 of 13",
"item 13 of 13",
]
.iter()
.map(|it| it.to_string())
.collect(),
5,
max_width_col_count,
SelectionMode::Multiple,
StyleSheet::default(),
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("user_input: {user_input:?}");
});
}
fn multiple_select_single_item() {
let mut instructions = multi_select_instructions();
let header = AnsiStyledText {
text: " Please select one or more items",
style: &[
RStyle::Foreground(Color::Rgb(171, 204, 242)),
RStyle::Background(Color::Rgb(31, 36, 46)),
],
};
instructions.push(vec![header]);
let list = vec![format!("one element")];
let user_input = select_from_list_with_multi_line_header(
instructions,
list,
Some(6),
None,
SelectionMode::Multiple,
StyleSheet::default(),
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
}
fn multiple_select_13_items_vph_5(
max_height_row_count: usize,
max_width_col_count: usize,
style: StyleSheet,
) {
let mut instructions = multi_select_instructions();
let header = AnsiStyledText {
text: " Please select one or more items",
style: &[
RStyle::Foreground(Color::Rgb(229, 239, 123)),
RStyle::Background(Color::Rgb(31, 36, 46)),
],
};
instructions.push(vec![header]);
let user_input = select_from_list_with_multi_line_header(
instructions,
[
"item 1 of 13",
"item 2 of 13",
"item 3 of 13",
"item 4 of 13",
"item 5 of 13",
"item 6 of 13",
"item 7 of 13",
"item 8 of 13",
"item 9 of 13",
"item 10 of 13",
"item 11 of 13",
"item 12 of 13",
"item 13 of 13",
]
.iter()
.map(|it| it.to_string())
.collect(),
Some(max_height_row_count),
Some(max_width_col_count),
SelectionMode::Multiple,
style,
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("user_input: {user_input:?}");
});
}
fn multiple_select_2_items_vph_5(
max_height_row_count: usize,
max_width_col_count: usize,
style: StyleSheet,
) {
let mut instructions = multi_select_instructions();
let header = AnsiStyledText {
text: " Please select one or more items",
style: &[
RStyle::Foreground(Color::Rgb(229, 239, 123)),
RStyle::Background(Color::Rgb(31, 36, 46)),
],
};
instructions.push(vec![header]);
let user_input = select_from_list_with_multi_line_header(
instructions,
["item 1 of 2", "item 2 of 2"]
.iter()
.map(|it| it.to_string())
.collect(),
Some(max_height_row_count),
Some(max_width_col_count),
SelectionMode::Multiple,
style,
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("user_input: {user_input:?}");
});
}
fn single_select_13_items_vph_5(
max_height_row_count: usize,
max_width_col_count: usize,
style: StyleSheet,
) {
let user_input = select_from_list(
"Single select".to_string(),
[
"item 1 of 13",
"item 2 of 13",
"item 3 of 13",
"item 4 of 13",
"item 5 of 13",
"item 6 of 13",
"item 7 of 13",
"item 8 of 13",
"item 9 of 13",
"item 10 of 10",
"item 11 of 13",
"item 12 of 13",
"item 13 of 13",
]
.iter()
.map(|it| it.to_string())
.collect(),
max_height_row_count,
max_width_col_count,
SelectionMode::Single,
style,
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("user_input: {user_input:?}");
});
}
fn single_select_2_items_vph_5(
max_height_row_count: usize,
max_width_col_count: usize,
style: StyleSheet,
) {
let mut instructions = single_select_instruction();
let header = AnsiStyledText {
text: " Please select one item",
style: &[
RStyle::Foreground(Color::Rgb(171, 204, 242)),
RStyle::Background(Color::Rgb(31, 36, 46)),
],
};
instructions.push(vec![header]);
let user_input = select_from_list_with_multi_line_header(
instructions,
["item 1 of 2", "item 2 of 2"]
.iter()
.map(|it| it.to_string())
.collect(),
Some(max_height_row_count),
Some(max_width_col_count),
SelectionMode::Single,
style,
);
match &user_input {
Some(it) => {
println!("User selected: {:?}", it);
}
None => println!("User did not select anything"),
}
call_if_true!(DEVELOPMENT_MODE, {
tracing::debug!("user_input: {user_input:?}");
});
}
fn multi_select_instructions() -> Vec<Vec<AnsiStyledText<'static>>> {
let up_and_down = AnsiStyledText {
text: " Up or down:",
style: &[
RStyle::Foreground(Color::Rgb(9, 238, 211)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let navigate = AnsiStyledText {
text: " navigate",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_1 = vec![up_and_down, navigate];
let space = AnsiStyledText {
text: " Space:",
style: &[
RStyle::Foreground(Color::Rgb(255, 216, 9)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let select = AnsiStyledText {
text: " select or deselect item",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_2 = vec![space, select];
let esc = AnsiStyledText {
text: " Esc or Ctrl+C:",
style: &[
RStyle::Foreground(Color::Rgb(255, 132, 18)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let exit = AnsiStyledText {
text: " exit program",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_3 = vec![esc, exit];
let return_key = AnsiStyledText {
text: " Return:",
style: &[
RStyle::Foreground(Color::Rgb(234, 0, 196)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let confirm = AnsiStyledText {
text: " confirm selection",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_4 = vec![return_key, confirm];
vec![line_1, line_2, line_3, line_4]
}
fn single_select_instruction() -> Vec<Vec<AnsiStyledText<'static>>> {
let up_and_down = AnsiStyledText {
text: " Up or down:",
style: &[
RStyle::Foreground(Color::Rgb(9, 238, 211)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let navigate = AnsiStyledText {
text: " navigate",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_1 = vec![up_and_down, navigate];
let esc = AnsiStyledText {
text: " Esc or Ctrl+C:",
style: &[
RStyle::Foreground(Color::Rgb(255, 132, 18)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let exit = AnsiStyledText {
text: " exit program",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_2 = vec![esc, exit];
let return_key = AnsiStyledText {
text: " Return:",
style: &[
RStyle::Foreground(Color::Rgb(234, 0, 196)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let confirm = AnsiStyledText {
text: " confirm selection",
style: &[
RStyle::Foreground(Color::Rgb(94, 103, 111)),
RStyle::Background(Color::Rgb(14, 17, 23)),
],
};
let line_3 = vec![return_key, confirm];
vec![line_1, line_2, line_3]
}