Compare commits

..

2 Commits

Author SHA1 Message Date
Thomas Eppers
063ce06aaa show errors for next/previous page in info widget 2021-09-08 19:26:07 +02:00
Thomas Eppers
d79baa67c6 added new widget to show errors and keybindings 2021-09-08 18:58:21 +02:00
4 changed files with 81 additions and 29 deletions

View File

@ -8,6 +8,7 @@ use tui::backend::TermionBackend;
use tui::layout::{Constraint, Direction, Layout}; use tui::layout::{Constraint, Direction, Layout};
use tui::Terminal; use tui::Terminal;
use crate::widget::info;
use crate::widget::repo_entry; use crate::widget::repo_entry;
use crate::widget::service_switcher; use crate::widget::service_switcher;
use crate::widget::tag_list; use crate::widget::tag_list;
@ -17,6 +18,7 @@ pub struct Ui {
repo: crate::widget::repo_entry::RepoEntry, repo: crate::widget::repo_entry::RepoEntry,
tags: crate::widget::tag_list::TagList, tags: crate::widget::tag_list::TagList,
services: crate::widget::service_switcher::ServiceSwitcher, services: crate::widget::service_switcher::ServiceSwitcher,
info: crate::widget::info::Info,
} }
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone)]
@ -26,13 +28,16 @@ pub enum State {
SelectService, SelectService,
} }
impl State { impl std::iter::Iterator for State {
fn next(&self) -> State { type Item = Self;
fn next(&mut self) -> Option<Self::Item> {
match self { match self {
State::EditRepo => State::SelectTag, State::EditRepo => *self = State::SelectTag,
State::SelectTag => State::SelectService, State::SelectTag => *self = State::SelectService,
State::SelectService => State::EditRepo, State::SelectService => *self = State::EditRepo,
} }
Some(self.clone())
} }
} }
@ -41,8 +46,9 @@ impl Ui {
let mut ui = Ui { let mut ui = Ui {
state: State::SelectService, state: State::SelectService,
repo: repo_entry::RepoEntry::new(repo_id), repo: repo_entry::RepoEntry::new(repo_id),
tags: tag_list::TagList::with_status("Select image or edit Repository"), tags: tag_list::TagList::with_status("Tags are empty"),
services: service_switcher::ServiceSwitcher::new(), services: service_switcher::ServiceSwitcher::new(),
info: info::Info::new("Select image of edit Repository"),
}; };
//setup tui //setup tui
@ -62,9 +68,10 @@ impl Ui {
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints( .constraints(
[ [
Constraint::Min(3), Constraint::Min(9),
Constraint::Length(3), Constraint::Length(3),
Constraint::Max(7), Constraint::Min(7),
Constraint::Length(2),
] ]
.as_ref(), .as_ref(),
) )
@ -75,26 +82,36 @@ impl Ui {
rect.render_widget(ui.repo.render(&ui.state), chunks[1]); rect.render_widget(ui.repo.render(&ui.state), chunks[1]);
let (list, state) = ui.tags.render(&ui.state); let (list, state) = ui.tags.render(&ui.state);
rect.render_stateful_widget(list, chunks[2], state); rect.render_stateful_widget(list, chunks[2], state);
rect.render_widget(ui.info.render(), chunks[3]);
}) })
.unwrap(); .unwrap();
//handle input //handle input
match receiver.try_recv() { match receiver.try_recv() {
Ok(Key::Ctrl('q')) => break 'core, //quit program without saving Ok(Key::Ctrl('q')) => break 'core, //quit program without saving
Ok(Key::Char('\t')) => ui.state = ui.state.next(), Ok(Key::Char('\t')) => {
ui.state.next();
()
}
Ok(Key::Ctrl('s')) => match ui.services.save() { Ok(Key::Ctrl('s')) => match ui.services.save() {
Err(e) => { Err(e) => {
ui.show_info(&format!("{}", e)); ui.info.set_info(&format!("{}", e));
continue; continue;
} }
Ok(_) => (), Ok(_) => ui.info.set_info("Saved compose file"),
}, },
Ok(Key::Ctrl('r')) => { Ok(Key::Ctrl('r')) => {
ui.repo.confirm(); ui.repo.confirm();
ui.tags = tag_list::TagList::with_repo(ui.repo.get()); ui.tags = tag_list::TagList::with_repo(ui.repo.get());
} }
Ok(Key::Ctrl('n')) => ui.tags.next_page(), Ok(Key::Ctrl('n')) => match ui.tags.next_page() {
Ok(Key::Ctrl('p')) => ui.tags.prev_page(), Err(e) => ui.info.set_info(&format!("{}", e)),
Ok(_) => (),
},
Ok(Key::Ctrl('p')) => match ui.tags.prev_page() {
Err(e) => ui.info.set_info(&format!("{}", e)),
Ok(_) => (),
},
Ok(Key::Char('\n')) => match ui.state { Ok(Key::Char('\n')) => match ui.state {
State::EditRepo => { State::EditRepo => {
ui.repo.confirm(); ui.repo.confirm();
@ -104,7 +121,7 @@ impl Ui {
let mut repo = ui.repo.get(); let mut repo = ui.repo.get();
let tag = match ui.tags.get_selected() { let tag = match ui.tags.get_selected() {
Err(e) => { Err(e) => {
ui.show_info(&format!("{}", e)); ui.info.set_info(&format!("{}", e));
continue; continue;
} }
Ok(tag) => tag, Ok(tag) => tag,
@ -117,14 +134,14 @@ impl Ui {
}, },
Ok(Key::Char(key)) => { Ok(Key::Char(key)) => {
if ui.state == State::EditRepo { if ui.state == State::EditRepo {
ui.show_info("Editing Repository"); ui.info.set_info("Editing Repository");
} }
ui.repo.handle_input(&ui.state, Key::Char(key)); ui.repo.handle_input(&ui.state, Key::Char(key));
ui.tags.handle_input(&ui.state, Key::Char(key)); ui.tags.handle_input(&ui.state, Key::Char(key));
} }
Ok(Key::Backspace) => { Ok(Key::Backspace) => {
if ui.state == State::EditRepo { if ui.state == State::EditRepo {
ui.show_info("Editing Repository"); ui.info.set_info("Editing Repository");
} }
ui.repo.handle_input(&ui.state, Key::Backspace); ui.repo.handle_input(&ui.state, Key::Backspace);
ui.tags.handle_input(&ui.state, Key::Backspace); ui.tags.handle_input(&ui.state, Key::Backspace);
@ -132,11 +149,11 @@ impl Ui {
Ok(Key::Up) => { Ok(Key::Up) => {
if ui.state == State::SelectService && ui.services.find_previous_match() { if ui.state == State::SelectService && ui.services.find_previous_match() {
match ui.services.extract_repo() { match ui.services.extract_repo() {
Err(e) => ui.show_info(&format!("{}", e)), Err(e) => ui.info.set_info(&format!("{}", e)),
Ok(s) => { Ok(s) => {
let repo = match crate::tags::Tags::check_repo(s) { let repo = match crate::tags::Tags::check_repo(s) {
Err(e) => { Err(e) => {
ui.show_info(&format!("{}", e)); ui.info.set_info(&format!("{}", e));
continue; continue;
} }
Ok(s) => s, Ok(s) => s,
@ -152,11 +169,11 @@ impl Ui {
Ok(Key::Down) => match ui.state { Ok(Key::Down) => match ui.state {
State::SelectService if ui.services.find_next_match() => { State::SelectService if ui.services.find_next_match() => {
match ui.services.extract_repo() { match ui.services.extract_repo() {
Err(e) => ui.show_info(&format!("{}", e)), Err(e) => ui.info.set_info(&format!("{}", e)),
Ok(s) => { Ok(s) => {
let repo = match crate::tags::Tags::check_repo(s) { let repo = match crate::tags::Tags::check_repo(s) {
Err(e) => { Err(e) => {
ui.show_info(&format!("{}", e)); ui.info.set_info(&format!("{}", e));
continue; continue;
} }
Ok(s) => s, Ok(s) => s,
@ -185,11 +202,6 @@ impl Ui {
terminal.clear().unwrap(); terminal.clear().unwrap();
} }
/// helper function to show information in TagList
fn show_info(&mut self, error: &str) {
self.tags = tag_list::TagList::with_status(error);
}
/// create a thread for catching input and send them to core loop /// create a thread for catching input and send them to core loop
pub fn spawn_stdin_channel(&self) -> mpsc::Receiver<termion::event::Key> { pub fn spawn_stdin_channel(&self) -> mpsc::Receiver<termion::event::Key> {
let (tx, rx) = mpsc::channel::<termion::event::Key>(); let (tx, rx) = mpsc::channel::<termion::event::Key>();

33
src/widget/info.rs Normal file
View File

@ -0,0 +1,33 @@
use tui::style::{Color, Style};
use tui::widgets::{Block, List, ListItem};
pub struct Info {
info: String,
keys: String,
}
impl Info {
pub fn new(info: &str) -> Self {
Self {
info: String::from(info),
keys: String::from(
"Tab Cycle widgets C-s Save C-r Reload C-q Quit C-n Next page C-p Previous page ↑ ↓ Select tags or image line",
),
}
}
pub fn render(&self) -> List {
let items = vec![
ListItem::new(self.info.clone()),
ListItem::new(self.keys.clone()),
];
List::new(items)
.block(Block::default())
.style(Style::default().fg(Color::White).bg(Color::Black))
.highlight_style(Style::default().bg(Color::Black))
}
pub fn set_info(&mut self, info: &str) {
self.info = String::from(info);
}
}

View File

@ -1,3 +1,4 @@
pub mod info;
pub mod repo_entry; pub mod repo_entry;
pub mod service_switcher; pub mod service_switcher;
pub mod tag_list; pub mod tag_list;

View File

@ -11,6 +11,8 @@ use crate::ui::State;
pub enum Error { pub enum Error {
NoneSelected, NoneSelected,
NoTags, NoTags,
NoNextPage,
NoPrevPage,
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -18,6 +20,8 @@ impl fmt::Display for Error {
match self { match self {
Error::NoTags => write!(f, "There are no tags"), Error::NoTags => write!(f, "There are no tags"),
Error::NoneSelected => write!(f, "No tag selected"), Error::NoneSelected => write!(f, "No tag selected"),
Error::NoNextPage => write!(f, "No next page available"),
Error::NoPrevPage => write!(f, "No previous page available"),
} }
} }
} }
@ -55,25 +59,27 @@ impl TagList {
} }
/// display next page if possible /// display next page if possible
pub fn next_page(&mut self) { pub fn next_page(&mut self) -> Result<(), Error> {
match &self.typ { match &self.typ {
Type::Status(_) => (), Type::Status(_) => (),
Type::Repo(tags) => match tags.next_page() { Type::Repo(tags) => match tags.next_page() {
Err(e) => self.typ = Type::Status(format!("{}", e)), Err(e) => return Err(Error::NoNextPage),
Ok(tags) => self.typ = Type::Repo(tags), Ok(tags) => self.typ = Type::Repo(tags),
}, },
} }
Ok(())
} }
/// display previous page if possible /// display previous page if possible
pub fn prev_page(&mut self) { pub fn prev_page(&mut self) -> Result<(), Error> {
match &self.typ { match &self.typ {
Type::Status(_) => (), Type::Status(_) => (),
Type::Repo(tags) => match tags.prev_page() { Type::Repo(tags) => match tags.prev_page() {
Err(e) => self.typ = Type::Status(format!("{}", e)), Err(e) => return Err(Error::NoPrevPage),
Ok(tags) => self.typ = Type::Repo(tags), Ok(tags) => self.typ = Type::Repo(tags),
}, },
} }
Ok(())
} }
/// get a list of tag names with info /// get a list of tag names with info