-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
use promkit::{error::Result, preset::Checkbox}; | ||
|
||
fn main() -> Result { | ||
let mut p = Checkbox::new(0..100) | ||
.title("What number do you like?") | ||
.lines(5) | ||
.prompt()?; | ||
println!("result: {:?}", p.run()?); | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod checkbox; | ||
mod cursor; | ||
pub mod menu; | ||
pub mod text; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use std::{collections::HashSet, fmt, iter::FromIterator}; | ||
|
||
mod render; | ||
pub use render::Renderer; | ||
mod build; | ||
pub use build::Builder; | ||
|
||
use crate::core::menu::Menu; | ||
|
||
#[derive(Clone)] | ||
pub struct Checkbox { | ||
menu: Menu, | ||
picked: HashSet<usize>, | ||
} | ||
|
||
impl<T: fmt::Display> FromIterator<T> for Checkbox { | ||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { | ||
Self { | ||
menu: Menu::from_iter(iter), | ||
picked: HashSet::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Checkbox { | ||
pub fn items(&self) -> &Vec<String> { | ||
self.menu.items() | ||
} | ||
|
||
pub fn position(&self) -> usize { | ||
self.menu.position() | ||
} | ||
|
||
pub fn picked_indexes(&self) -> &HashSet<usize> { | ||
&self.picked | ||
} | ||
|
||
pub fn get(&self) -> Vec<String> { | ||
self.picked | ||
.iter() | ||
.fold(Vec::<String>::new(), |mut ret, idx| { | ||
ret.push(self.menu.items().get(*idx).unwrap().to_owned()); | ||
ret | ||
}) | ||
} | ||
|
||
pub fn toggle(&mut self) { | ||
if self.picked.contains(&self.menu.position()) { | ||
self.picked.remove(&self.menu.position()); | ||
} else { | ||
self.picked.insert(self.menu.position()); | ||
} | ||
} | ||
|
||
pub fn backward(&mut self) -> bool { | ||
self.menu.backward() | ||
} | ||
|
||
pub fn forward(&mut self) -> bool { | ||
self.menu.forward() | ||
} | ||
|
||
pub fn move_to_head(&mut self) { | ||
self.menu.move_to_head() | ||
} | ||
|
||
pub fn move_to_tail(&mut self) { | ||
self.menu.move_to_tail() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use std::{fmt::Display, iter::FromIterator}; | ||
|
||
use crate::{crossterm::style::ContentStyle, error::Result, render::State}; | ||
|
||
use super::{Checkbox, Renderer}; | ||
|
||
#[derive(Clone)] | ||
pub struct Builder { | ||
checkbox: Checkbox, | ||
style: ContentStyle, | ||
mark: String, | ||
cursor: String, | ||
cursor_style: ContentStyle, | ||
lines: Option<usize>, | ||
} | ||
|
||
impl Builder { | ||
pub fn new<T: Display, I: IntoIterator<Item = T>>(items: I) -> Self { | ||
Self { | ||
checkbox: Checkbox::from_iter(items), | ||
style: Default::default(), | ||
mark: Default::default(), | ||
cursor: Default::default(), | ||
cursor_style: Default::default(), | ||
lines: Default::default(), | ||
} | ||
} | ||
|
||
pub fn mark<T: AsRef<str>>(mut self, mark: T) -> Self { | ||
self.mark = mark.as_ref().to_string(); | ||
self | ||
} | ||
|
||
pub fn style(mut self, style: ContentStyle) -> Self { | ||
self.style = style; | ||
self | ||
} | ||
|
||
pub fn cursor<T: AsRef<str>>(mut self, cursor: T) -> Self { | ||
self.cursor = cursor.as_ref().to_string(); | ||
self | ||
} | ||
|
||
pub fn cursor_style(mut self, style: ContentStyle) -> Self { | ||
self.cursor_style = style; | ||
self | ||
} | ||
|
||
pub fn lines(mut self, lines: usize) -> Self { | ||
self.lines = Some(lines); | ||
self | ||
} | ||
|
||
pub fn build(self) -> Result<Renderer> { | ||
Ok(Renderer { | ||
checkbox: self.checkbox, | ||
mark: self.mark, | ||
style: self.style, | ||
cursor: self.cursor, | ||
cursor_style: self.cursor_style, | ||
lines: self.lines, | ||
}) | ||
} | ||
|
||
pub fn build_state(self) -> Result<Box<State<Renderer>>> { | ||
Ok(Box::new(State::<Renderer>::new(self.build()?))) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use std::any::Any; | ||
|
||
use crate::{ | ||
crossterm::{ | ||
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}, | ||
style::ContentStyle, | ||
}, | ||
grapheme::{trim, Graphemes}, | ||
pane::Pane, | ||
render::{AsAny, Renderable}, | ||
}; | ||
|
||
use super::Checkbox; | ||
|
||
#[derive(Clone)] | ||
pub struct Renderer { | ||
pub checkbox: Checkbox, | ||
|
||
pub style: ContentStyle, | ||
pub cursor: String, | ||
pub cursor_style: ContentStyle, | ||
pub mark: String, | ||
pub lines: Option<usize>, | ||
} | ||
|
||
impl Renderable for Renderer { | ||
fn make_pane(&self, width: u16) -> Pane { | ||
let f = |idx: usize, item: &String| -> String { | ||
if self.checkbox.picked_indexes().contains(&idx) { | ||
format!("[{}] {}", self.mark, item) | ||
} else { | ||
format!( | ||
"[{}] {}", | ||
" ".repeat(Graphemes::new(self.mark.clone()).widths()), | ||
item | ||
) | ||
} | ||
}; | ||
|
||
let matrix = self | ||
.checkbox | ||
.items() | ||
.iter() | ||
.enumerate() | ||
.map(|(i, item)| { | ||
if i == self.checkbox.position() { | ||
Graphemes::new_with_style( | ||
format!("{}{}", self.cursor, f(i, item)), | ||
self.cursor_style, | ||
) | ||
} else { | ||
Graphemes::new_with_style( | ||
format!( | ||
"{}{}", | ||
" ".repeat(Graphemes::new(self.cursor.clone()).widths()), | ||
f(i, item) | ||
), | ||
self.style, | ||
) | ||
} | ||
}) | ||
.collect::<Vec<Graphemes>>(); | ||
|
||
let trimed = matrix.iter().map(|row| trim(width as usize, row)).collect(); | ||
|
||
Pane::new(trimed, self.checkbox.position(), self.lines) | ||
} | ||
|
||
/// Default key bindings for item picker. | ||
/// | ||
/// | Key | Description | ||
/// | :-- | :-- | ||
/// | <kbd> Enter </kbd> | Exit the event-loop | ||
/// | <kbd> CTRL + C </kbd> | Exit the event-loop with an error | ||
/// | <kbd> ↑ </kbd> | Move backward | ||
/// | <kbd> ↓ </kbd> | Move forward | ||
/// | <kbd> CTRL + A </kbd> | Move to the beginning of the items | ||
/// | <kbd> CTRL + E </kbd> | Move to the end of the items | ||
fn handle_event(&mut self, event: &Event) { | ||
match event { | ||
// Move cursor. | ||
Event::Key(KeyEvent { | ||
code: KeyCode::Up, | ||
modifiers: KeyModifiers::NONE, | ||
kind: KeyEventKind::Press, | ||
state: KeyEventState::NONE, | ||
}) => { | ||
self.checkbox.backward(); | ||
} | ||
Event::Key(KeyEvent { | ||
code: KeyCode::Down, | ||
modifiers: KeyModifiers::NONE, | ||
kind: KeyEventKind::Press, | ||
state: KeyEventState::NONE, | ||
}) => { | ||
self.checkbox.forward(); | ||
} | ||
Event::Key(KeyEvent { | ||
code: KeyCode::Char('a'), | ||
modifiers: KeyModifiers::CONTROL, | ||
kind: KeyEventKind::Press, | ||
state: KeyEventState::NONE, | ||
}) => self.checkbox.move_to_head(), | ||
Event::Key(KeyEvent { | ||
code: KeyCode::Char('e'), | ||
modifiers: KeyModifiers::CONTROL, | ||
kind: KeyEventKind::Press, | ||
state: KeyEventState::NONE, | ||
}) => self.checkbox.move_to_tail(), | ||
Event::Key(KeyEvent { | ||
code: KeyCode::Char(' '), | ||
modifiers: KeyModifiers::NONE, | ||
kind: KeyEventKind::Press, | ||
state: KeyEventState::NONE, | ||
}) => self.checkbox.toggle(), | ||
|
||
_ => (), | ||
} | ||
} | ||
|
||
fn postrun(&mut self) { | ||
self.checkbox.move_to_head() | ||
} | ||
} | ||
|
||
impl AsAny for Renderer { | ||
fn as_any(&self) -> &dyn Any { | ||
self | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
mod checkbox; | ||
pub use checkbox::Checkbox; | ||
mod confirm; | ||
pub use confirm::Confirm; | ||
mod readline; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use std::fmt::Display; | ||
|
||
use crate::{ | ||
checkbox::{Builder as MenuRendererBuilder, Renderer as MenuRenderer}, | ||
error::Result, | ||
preset::theme::checkbox::Theme, | ||
render::{Renderable, State}, | ||
text::Builder as TextRendererBuilder, | ||
Prompt, | ||
}; | ||
|
||
pub struct Checkbox { | ||
title_builder: TextRendererBuilder, | ||
menu_builder: MenuRendererBuilder, | ||
} | ||
|
||
impl Checkbox { | ||
pub fn new<T: Display, I: IntoIterator<Item = T>>(items: I) -> Self { | ||
Self { | ||
title_builder: Default::default(), | ||
menu_builder: MenuRendererBuilder::new(items), | ||
} | ||
.theme(Theme::default()) | ||
} | ||
|
||
pub fn theme(mut self, theme: Theme) -> Self { | ||
self.title_builder = self.title_builder.style(theme.title_style); | ||
self.menu_builder = self | ||
.menu_builder | ||
.mark(theme.mark) | ||
.cursor(theme.cursor) | ||
.style(theme.item_style) | ||
.cursor_style(theme.cursor_style); | ||
self | ||
} | ||
|
||
pub fn title<T: AsRef<str>>(mut self, text: T) -> Self { | ||
self.title_builder = self.title_builder.text(text); | ||
self | ||
} | ||
|
||
pub fn lines(mut self, lines: usize) -> Self { | ||
self.menu_builder = self.menu_builder.lines(lines); | ||
self | ||
} | ||
|
||
pub fn prompt(self) -> Result<Prompt<Vec<String>>> { | ||
Prompt::try_new( | ||
vec![ | ||
self.title_builder.build_state()?, | ||
self.menu_builder.build_state()?, | ||
], | ||
|_, _| Ok(true), | ||
|renderables: &Vec<Box<dyn Renderable + 'static>>| -> Result<Vec<String>> { | ||
Ok(renderables[1] | ||
.as_any() | ||
.downcast_ref::<State<MenuRenderer>>() | ||
.unwrap() | ||
.after | ||
.borrow() | ||
.checkbox | ||
.get()) | ||
}, | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod checkbox; | ||
pub mod confirm; | ||
pub mod password; | ||
pub mod queryselect; | ||
|
Oops, something went wrong.