aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/gui.rs98
-rw-r--r--src/gui/message.rs7
-rw-r--r--src/gui/mod.rs95
-rw-r--r--src/gui/question.rs90
-rw-r--r--src/gui/util.rs2
5 files changed, 140 insertions, 152 deletions
diff --git a/src/gui/gui.rs b/src/gui/gui.rs
deleted file mode 100644
index 92b1a72..0000000
--- a/src/gui/gui.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use crate::deck;
-use crate::util::time;
-use crate::{db::db, gui::message, gui::question, space_repetition, util::event::Events};
-use anyhow::Result;
-use rusqlite::Connection;
-use std::{fs, io, time::Duration};
-use termion::{raw::IntoRawMode, raw::RawTerminal, screen::AlternateScreen};
-use tui::{backend::TermionBackend, Terminal};
-
-type Term = Terminal<TermionBackend<AlternateScreen<RawTerminal<io::Stdout>>>>;
-
-pub fn terminal() -> Result<Term> {
- let stdout = io::stdout().into_raw_mode()?;
- let stdout = AlternateScreen::from(stdout);
- let backend = TermionBackend::new(stdout);
- Ok(Terminal::new(backend)?)
-}
-
-pub fn synchronize(
- conn: &Connection,
- term: &mut Term,
- events: &Events,
- deck_path: &str,
- deck_name: &str,
-) -> Result<()> {
- let last_modified = time::seconds_since_unix_epoch_of(fs::metadata(deck_path)?.modified()?)?;
- let last_deck_read = db::last_deck_read(&conn);
- let must_synchronize = last_deck_read.map(|r| r < last_modified).unwrap_or(true);
-
- if must_synchronize {
- let _ = message::show(term, events, &deck_name, "Synchronization du deck…", false);
- time::wait_at_least(
- || db::synchronize(&conn, deck::read(&deck_path)?),
- Duration::from_secs(1),
- )?;
- }
-
- Ok(())
-}
-
-pub fn start(
- conn: &Connection,
- term: &mut Term,
- events: &Events,
- deck_name: &String,
-) -> Result<()> {
- let mut answers = 0;
-
- loop {
- let now = time::seconds_since_unix_epoch()?;
- let title = title(deck_name, answers, db::count_available(&conn).unwrap_or(0));
-
- match db::pick_random_ready(&conn) {
- Some(card) => {
- let difficulty = question::ask(term, events, &title, &card)?;
- answers += 1;
- db::update(
- &conn,
- &card.question,
- &space_repetition::update(card.state, difficulty),
- )?;
- }
- None => {
- let message = match db::next_ready(&conn) {
- Some(ready) => format!(
- "Prochaine carte disponible dans {}.",
- time::pp_duration(ready - now)
- ),
- None => format!("Aucune carte n’est disponible. Votre deck est-il vide ?"),
- };
- let _ = message::show(term, events, &title, &message, true);
- break;
- }
- }
- }
-
- Ok(())
-}
-
-fn title(deck_name: &String, answers: i32, available_cards: i32) -> String {
- if answers == 0 && available_cards == 0 {
- deck_name.to_string()
- } else if available_cards == 0 {
- format!(
- "{} ({} / {})",
- deck_name,
- answers,
- answers + available_cards
- )
- } else {
- format!(
- "{} ({} / {})",
- deck_name,
- answers + 1,
- answers + available_cards
- )
- }
-}
diff --git a/src/gui/message.rs b/src/gui/message.rs
index 28a1d2c..b938150 100644
--- a/src/gui/message.rs
+++ b/src/gui/message.rs
@@ -33,11 +33,8 @@ pub fn show<B: Backend>(
})?;
if wait {
- if let Event::Input(key) = events.next()? {
- match key {
- Key::Char('q') => break,
- _ => (),
- }
+ if let Event::Input(Key::Char('q')) = events.next()? {
+ break
}
} else {
break;
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index f351eba..92cd943 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -1,4 +1,97 @@
-pub mod gui;
pub mod message;
pub mod question;
pub mod util;
+
+use crate::deck;
+use crate::util::time;
+use crate::{db, space_repetition, util::event::Events};
+use anyhow::Result;
+use rusqlite::Connection;
+use std::{fs, io, time::Duration};
+use termion::{raw::IntoRawMode, raw::RawTerminal, screen::AlternateScreen};
+use tui::{backend::TermionBackend, Terminal};
+
+type Term = Terminal<TermionBackend<AlternateScreen<RawTerminal<io::Stdout>>>>;
+
+pub fn terminal() -> Result<Term> {
+ let stdout = io::stdout().into_raw_mode()?;
+ let stdout = AlternateScreen::from(stdout);
+ let backend = TermionBackend::new(stdout);
+ Ok(Terminal::new(backend)?)
+}
+
+pub fn synchronize(
+ conn: &Connection,
+ term: &mut Term,
+ events: &Events,
+ deck_path: &str,
+ deck_name: &str,
+) -> Result<()> {
+ let last_modified = time::seconds_since_unix_epoch_of(fs::metadata(deck_path)?.modified()?)?;
+ let last_deck_read = db::last_deck_read(conn);
+ let must_synchronize = last_deck_read.map(|r| r < last_modified).unwrap_or(true);
+
+ if must_synchronize {
+ let _ = message::show(term, events, deck_name, "Synchronization du deck…", false);
+ time::wait_at_least(
+ || db::synchronize(conn, deck::read(deck_path)?),
+ Duration::from_secs(1),
+ )?;
+ }
+
+ Ok(())
+}
+
+pub fn start(conn: &Connection, term: &mut Term, events: &Events, deck_name: &str) -> Result<()> {
+ let mut answers = 0;
+
+ loop {
+ let now = time::seconds_since_unix_epoch()?;
+ let title = title(deck_name, answers, db::count_available(conn).unwrap_or(0));
+
+ match db::pick_random_ready(conn) {
+ Some(card) => {
+ let difficulty = question::ask(term, events, &title, &card)?;
+ answers += 1;
+ db::update(
+ conn,
+ &card.question,
+ &space_repetition::update(card.state, difficulty),
+ )?;
+ }
+ None => {
+ let message = match db::next_ready(conn) {
+ Some(ready) => format!(
+ "Prochaine carte disponible dans {}.",
+ time::pp_duration(ready - now)
+ ),
+ None => "Aucune carte n’est disponible. Votre deck est-il vide ?".to_string(),
+ };
+ let _ = message::show(term, events, &title, &message, true);
+ break;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn title(deck_name: &str, answers: i32, available_cards: i32) -> String {
+ if answers == 0 && available_cards == 0 {
+ deck_name.to_string()
+ } else if available_cards == 0 {
+ format!(
+ "{} ({} / {})",
+ deck_name,
+ answers,
+ answers + available_cards
+ )
+ } else {
+ format!(
+ "{} ({} / {})",
+ deck_name,
+ answers + 1,
+ answers + available_cards
+ )
+ }
+}
diff --git a/src/gui/question.rs b/src/gui/question.rs
index 211bcda..5f060e3 100644
--- a/src/gui/question.rs
+++ b/src/gui/question.rs
@@ -28,7 +28,7 @@ enum Answer {
pub fn ask<B: Backend>(
terminal: &mut Terminal<B>,
events: &Events,
- title: &String,
+ title: &str,
card: &Card,
) -> Result<Difficulty> {
let mut state = State {
@@ -59,7 +59,7 @@ pub fn ask<B: Backend>(
let question = Paragraph::new(util::center_vertically(chunks[1], &card.question))
.style(match state.answer {
Answer::Write => {
- if state.input == "" {
+ if state.input.is_empty() {
Style::default().fg(Color::Yellow)
} else {
Style::default()
@@ -86,47 +86,44 @@ pub fn ask<B: Backend>(
.wrap(Wrap { trim: true });
f.render_widget(answer, chunks[2]);
- match state.answer {
- Answer::Difficulty {
- difficulty: selected,
- } => {
- if !is_correct(&state.input, &card.responses) || card.responses.len() > 1 {
- let paragraph = Paragraph::new(util::center_vertically(
- chunks[3],
- &serialization::words_to_line(&card.responses),
- ))
- .alignment(Alignment::Center);
- f.render_widget(paragraph, chunks[3]);
- };
+ if let Answer::Difficulty {
+ difficulty: selected,
+ } = state.answer
+ {
+ if !is_correct(&state.input, &card.responses) || card.responses.len() > 1 {
+ let paragraph = Paragraph::new(util::center_vertically(
+ chunks[3],
+ &serialization::words_to_line(&card.responses),
+ ))
+ .alignment(Alignment::Center);
+ f.render_widget(paragraph, chunks[3]);
+ };
- let difficulties = card.state.difficulties();
- let l = difficulties.len();
- let sep = Span::styled(" • ", Style::default());
- let tabs = difficulties
- .iter()
- .enumerate()
- .map(|(i, d)| {
- let style = if *d == selected {
- Style::default()
- .fg(Color::Yellow)
- .add_modifier(Modifier::UNDERLINED)
- } else {
- Style::default().add_modifier(Modifier::DIM)
- };
- let d = Span::styled(difficulty::label(*d), style);
- if i < l - 1 {
- [d, sep.clone()].to_vec()
- } else {
- [d].to_vec()
- }
- })
- .collect::<Vec<Vec<Span>>>()
- .concat();
- let p =
- Paragraph::new(Text::from(Spans::from(tabs))).alignment(Alignment::Center);
- f.render_widget(p, chunks[4]);
- }
- _ => {}
+ let difficulties = card.state.difficulties();
+ let l = difficulties.len();
+ let sep = Span::styled(" • ", Style::default());
+ let tabs = difficulties
+ .iter()
+ .enumerate()
+ .map(|(i, d)| {
+ let style = if *d == selected {
+ Style::default()
+ .fg(Color::Yellow)
+ .add_modifier(Modifier::UNDERLINED)
+ } else {
+ Style::default().add_modifier(Modifier::DIM)
+ };
+ let d = Span::styled(difficulty::label(*d), style);
+ if i < l - 1 {
+ [d, sep.clone()].to_vec()
+ } else {
+ [d].to_vec()
+ }
+ })
+ .collect::<Vec<Vec<Span>>>()
+ .concat();
+ let p = Paragraph::new(Text::from(Spans::from(tabs))).alignment(Alignment::Center);
+ f.render_widget(p, chunks[4]);
}
})?;
@@ -171,15 +168,14 @@ pub fn ask<B: Backend>(
}
}
-fn is_correct(input: &String, responses: &Vec<String>) -> bool {
+fn is_correct(input: &str, responses: &[String]) -> bool {
responses
.iter()
- .map(|r| r.split("(").collect::<Vec<&str>>()[0].trim())
- .collect::<Vec<&str>>()
- .contains(&input.as_str())
+ .map(|r| r.split('(').collect::<Vec<&str>>()[0].trim())
+ .any(|x| x == input)
}
-fn relative_element<T: Clone + PartialEq>(xs: &Vec<T>, x: &T, ri: i32) -> Option<T> {
+fn relative_element<T: Clone + PartialEq>(xs: &[T], x: &T, ri: i32) -> Option<T> {
let i = xs.iter().position(|t| t == x)? as i32 + ri;
if i >= 0 && i < xs.len() as i32 {
Some(xs[i as usize].clone())
diff --git a/src/gui/util.rs b/src/gui/util.rs
index 38ed1e7..2314aba 100644
--- a/src/gui/util.rs
+++ b/src/gui/util.rs
@@ -12,7 +12,7 @@ pub fn title(str: &str) -> Paragraph {
)
}
-pub fn center_vertically(chunk: Rect, text: &String) -> String {
+pub fn center_vertically(chunk: Rect, text: &str) -> String {
let text_lines = text.lines().count();
let chunk_inner_lines: usize = (chunk.height - 2).into();
let blank_lines = chunk_inner_lines - text_lines;