tts_tui/
app.rs

1use arboard::Clipboard;
2use std::error;
3use tts::{Features, Tts};
4
5pub type AppResult<T> = std::result::Result<T, Box<dyn error::Error>>;
6
7pub static mut PARAGRAPH: usize = 0;
8pub static mut VECTOR: Vec<&str> = Vec::new();
9pub static mut COPY: String = String::new();
10pub static mut LINE: u16 = 0;
11
12pub struct App {
13    pub running: bool,
14    pub clipboard: Clipboard,
15    pub tts: Tts,
16    pub pause: bool,
17    pub history: Vec<String>,
18    pub last_copy: String,
19    pub tab_length: u16,
20    pub selected: usize,
21    pub jump_length: u16,
22    pub last_paragraph: usize,
23    pub pdf_mode: bool,
24}
25
26impl Default for App {
27    fn default() -> Self {
28        Self {
29            running: true,
30            clipboard: Clipboard::new().unwrap(),
31            tts: Tts::default().unwrap(),
32            pause: false,
33            history: Vec::with_capacity(10),
34            last_copy: String::from(""),
35            tab_length: 3,
36            selected: 0,
37            jump_length: 15,
38            last_paragraph: 0,
39            pdf_mode: false,
40        }
41    }
42}
43
44impl App {
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    pub fn tick(&mut self) {
50        let Features {
51            utterance_callbacks,
52            ..
53        } = self.tts.supported_features();
54        if utterance_callbacks {
55            self.tts
56                .on_utterance_end(Some(Box::new(|_utterance| unsafe {
57                    if !VECTOR.is_empty() && PARAGRAPH < VECTOR.len() - 1 {
58                        PARAGRAPH += 1;
59                    }
60                })))
61                .unwrap();
62        }
63        match self.clipboard.get_text() {
64            Ok(contents) => unsafe {
65                // pause doesn't stop actions from starting tts intentionally
66                if self.pause {
67                    if self.tts.is_speaking().unwrap() {
68                        self.tts.stop().unwrap();
69                    } else {
70                        match self.pdf_mode {
71                            false => self.tts.speak(VECTOR[PARAGRAPH], true).unwrap(),
72                            true => self
73                                .tts
74                                .speak(VECTOR[PARAGRAPH].replace('\n', " "), true)
75                                .unwrap(),
76                        };
77                    }
78                    self.pause = false;
79                } else if self.last_copy != contents {
80                    self.last_copy = contents.clone();
81                    (self.last_paragraph, PARAGRAPH, LINE, self.selected) = (0, 0, 0, 0);
82                    COPY = contents.chars().filter(|&c| c != '\r').collect();
83                    VECTOR = COPY.split("\n\n").filter(|s| !s.is_empty()).collect();
84                    if !VECTOR.is_empty() {
85                        match self.pdf_mode {
86                            false => self.tts.speak(VECTOR[PARAGRAPH], true).unwrap(),
87                            true => self
88                                .tts
89                                .speak(VECTOR[PARAGRAPH].replace('\n', " "), true)
90                                .unwrap(),
91                        };
92                    }
93                    if self.history.len() > 9 {
94                        self.history.pop();
95                    }
96                    self.history.insert(0, contents);
97                } else if self.last_paragraph != PARAGRAPH {
98                    self.last_paragraph = PARAGRAPH;
99                    match self.pdf_mode {
100                        false => self.tts.speak(VECTOR[PARAGRAPH], true).unwrap(),
101                        true => self
102                            .tts
103                            .speak(VECTOR[PARAGRAPH].replace('\n', " "), true)
104                            .unwrap(),
105                    };
106                }
107            },
108            Err(_e) => (),
109        }
110    }
111
112    pub fn quit(&mut self) {
113        self.running = false;
114    }
115}