From ea52a2e90fbc19bd0fb8555aa7d3fb41de46030d Mon Sep 17 00:00:00 2001 From: Shashwat <10794178+TheTrio@users.noreply.github.com> Date: Sun, 1 May 2022 20:55:11 +0530 Subject: [PATCH] implemented basic scrolling --- src/main.rs | 22 +++++++++++++++------- src/thok.rs | 33 +++++++++++++++++++++++++++++++-- src/ui.rs | 10 ++++------ 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9c39535..0e42971 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use tui::{ backend::{Backend, CrosstermBackend}, Frame, Terminal, }; +use ui::HORIZONTAL_MARGIN; use webbrowser::Browser; const TICK_RATE_MS: u64 = 100; @@ -31,7 +32,7 @@ const TICK_RATE_MS: u64 = 100; #[clap(version, about, long_about= None)] pub struct Cli { /// number of words to use in test - #[clap(short = 'w', long, default_value_t = 15)] + #[clap(short = 'w', long, default_value_t = 2000)] number_of_words: usize, /// number of seconds to run test @@ -146,8 +147,7 @@ fn start_tui( loop { let mut exit_type: ExitType = ExitType::Quit; - terminal.draw(|f| ui(app, f))?; - + terminal.draw(|f| ui(app, f, false))?; loop { let app = &mut app; @@ -159,13 +159,14 @@ fn start_tui( if app.thok.has_finished() { app.thok.calc_results(); } - terminal.draw(|f| ui(app, f))?; + terminal.draw(|f| ui(app, f, false))?; } } ThokEvent::Resize => { - terminal.draw(|f| ui(app, f))?; + terminal.draw(|f| ui(app, f, false))?; } ThokEvent::Key(key) => { + let mut is_space = false; match key.code { KeyCode::Esc => { break; @@ -185,6 +186,9 @@ fn start_tui( } KeyCode::Char(c) => match app.thok.has_finished() { false => { + if c == ' ' { + is_space = true; + } app.thok.write(c); if app.thok.has_finished() { app.thok.calc_results(); @@ -210,7 +214,7 @@ fn start_tui( }, _ => {} } - terminal.draw(|f| ui(app, f))?; + terminal.draw(|f| ui(app, f, is_space))?; } } } @@ -267,6 +271,10 @@ fn get_thok_events(should_tick: bool) -> mpsc::Receiver { rx } -fn ui(app: &mut App, f: &mut Frame) { +fn ui(app: &mut App, f: &mut Frame, is_space: bool) { + if is_space { + app.thok + .get_skip_count((f.size().width - HORIZONTAL_MARGIN * 2).into()); + } f.render_widget(&app.thok, f.size()); } diff --git a/src/thok.rs b/src/thok.rs index c7ec39e..dd328f7 100644 --- a/src/thok.rs +++ b/src/thok.rs @@ -2,7 +2,7 @@ use crate::util::std_dev; use crate::TICK_RATE_MS; use chrono::prelude::*; use directories::ProjectDirs; -use itertools::Itertools; +use itertools::{any, Itertools}; use std::fs::OpenOptions; use std::io::{self, Write}; use std::{char, collections::HashMap, time::SystemTime}; @@ -35,10 +35,13 @@ pub struct Thok { pub wpm: f64, pub accuracy: f64, pub std_dev: f64, + pub skip: usize, + pub line: usize, } impl Thok { pub fn new(prompt: String, number_of_words: usize, number_of_secs: Option) -> Self { + // let prompt = prompt.replace(" ", "*"); Self { prompt, input: vec![], @@ -52,6 +55,8 @@ impl Thok { wpm: 0.0, accuracy: 0.0, std_dev: 0.0, + skip: 0, + line: 0, } } @@ -151,7 +156,7 @@ impl Thok { } pub fn backspace(&mut self) { - if self.cursor_pos > 0 { + if self.cursor_pos > 0 && self.cursor_pos > self.skip { self.input.remove(self.cursor_pos - 1); self.decrement_cursor(); } @@ -234,4 +239,28 @@ impl Thok { Ok(()) } + + pub fn get_skip_count(&mut self, max_width: usize) { + if any(&self.input[self.skip..], |x| { + x.outcome == Outcome::Incorrect + }) { + return; + } + let count = self.cursor_pos - self.skip; + if count == 0 { + self.skip += max_width; + self.line += 1; + return; + } + let rest = &self.prompt[self.cursor_pos..]; + let index = rest.find(' '); + if let Some(index) = index { + let next_word = &rest[..index]; + let next_word_len = next_word.len(); + if count + next_word_len > max_width { + self.skip += count; + self.line += 1; + } + } + } } diff --git a/src/ui.rs b/src/ui.rs index f91fe96..8162750 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -10,7 +10,7 @@ use webbrowser::Browser; use crate::thok::{Outcome, Thok}; -const HORIZONTAL_MARGIN: u16 = 5; +pub const HORIZONTAL_MARGIN: u16 = 5; const VERTICAL_MARGIN: u16 = 2; impl Widget for &Thok { @@ -40,11 +40,9 @@ impl Widget for &Thok { ((self.prompt.width() as f64 / max_chars_per_line as f64).ceil() + 1.0) as u16; let time_left_lines = if self.number_of_secs.is_some() { 2 } else { 0 }; - if self.prompt.width() <= max_chars_per_line as usize { prompt_occupied_lines = 1; } - let chunks = Layout::default() .direction(Direction::Vertical) .horizontal_margin(HORIZONTAL_MARGIN) @@ -54,7 +52,7 @@ impl Widget for &Thok { ((area.height as f64 - prompt_occupied_lines as f64) / 2.0) as u16, ), Constraint::Length(time_left_lines), - Constraint::Length(prompt_occupied_lines), + Constraint::Length(3), Constraint::Length( ((area.height as f64 - prompt_occupied_lines as f64) / 2.0) as u16, ), @@ -62,14 +60,14 @@ impl Widget for &Thok { .as_ref(), ) .split(area); - let mut spans = self .input .iter() + .skip(self.skip) .enumerate() .map(|(idx, input)| { Span::styled( - self.get_expected_char(idx).to_string(), + self.get_expected_char(self.skip + idx).to_string(), match input.outcome { Outcome::Correct => green_bold_style, Outcome::Incorrect => red_bold_style,