Krolyxon
Commit 38ebdf9

rmptuisrcapp.rs

288 lines · 9.4 KB · Rust

1use std::time::Duration;
2
3use crate::browser::FileBrowser;
4use crate::connection::Connection;
5use crate::list::ContentList;
6use crate::ui::InputMode;
7use mpd::Client;
8
9// Application result type
10pub type AppResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
11
12/// Application
13#[derive(Debug)]
14pub struct App {
15 /// check if app is running
16 pub running: bool,
17 pub conn: Connection,
18 pub queue_list: ContentList<String>,
19 pub pl_list: ContentList<String>,
20 pub selected_tab: SelectedTab,
21 pub browser: FileBrowser,
22
23 // Search
24 pub inputmode: InputMode,
25 pub search_input: String,
26 pub cursor_position: usize,
27
28 // add to playlists
29 pub playlist_popup: bool,
30 pub append_list: ContentList<String>,
31}
32
33#[derive(Debug, PartialEq, Clone)]
34pub enum SelectedTab {
35 DirectoryBrowser,
36 Queue,
37 Playlists,
38}
39
40impl App {
41 pub fn builder(addrs: &str) -> AppResult<Self> {
42 let mut conn = Connection::new(addrs).unwrap();
43 let mut queue_list = ContentList::new();
44 let mut pl_list = ContentList::new();
45 pl_list.list = Self::get_playlist(&mut conn.conn)?;
46
47 let append_list = Self::get_append_list(&mut conn.conn)?;
48 Self::get_queue(&mut conn, &mut queue_list.list);
49
50 let browser = FileBrowser::new();
51
52 Ok(Self {
53 running: true,
54 conn,
55 queue_list,
56 pl_list,
57 selected_tab: SelectedTab::Queue,
58 browser,
59 inputmode: InputMode::Normal,
60 search_input: String::new(),
61 cursor_position: 0,
62 playlist_popup: false,
63 append_list,
64 })
65 }
66
67 pub fn tick(&mut self) {
68 self.conn.update_status();
69 self.update_queue();
70 }
71
72 pub fn quit(&mut self) {
73 self.running = false;
74 }
75
76 pub fn get_queue(conn: &mut Connection, vec: &mut Vec<String>) {
77 // conn.conn.queue().unwrap().into_iter().for_each(|x| {
78 // if let Some(title) = x.title {
79 // if let Some(artist) = x.artist {
80 // vec.push(format!("{} - {}", artist, title));
81 // } else {
82 // vec.push(title)
83 // }
84 // } else {
85 // vec.push(x.file)
86 // }
87 // });
88 conn.conn.queue().unwrap().into_iter().for_each(|x| {
89 vec.push(x.file);
90 });
91 }
92
93 pub fn update_queue(&mut self) {
94 self.queue_list.list.clear();
95 Self::get_queue(&mut self.conn, &mut self.queue_list.list);
96 }
97
98 pub fn get_playlist(conn: &mut Client) -> AppResult<Vec<String>> {
99 let list: Vec<String> = conn.playlists()?.iter().map(|p| p.clone().name).collect();
100 Ok(list)
101 }
102
103 pub fn get_append_list(conn: &mut Client) -> AppResult<ContentList<String>> {
104 let mut list = ContentList::new();
105 list.list.push("Current Playlist".to_string());
106 for item in Self::get_playlist(conn)? {
107 list.list.push(item.to_string());
108 }
109
110 Ok(list)
111 }
112
113 pub fn update_playlist(&mut self) -> AppResult<()> {
114 Self::get_playlist(&mut self.conn.conn)?;
115 Ok(())
116 }
117
118 pub fn handle_add_or_remove_from_current_playlist(&mut self) -> AppResult<()> {
119 match self.selected_tab {
120 SelectedTab::DirectoryBrowser => {
121 let (_, file) = self.browser.filetree.get(self.browser.selected).unwrap();
122
123 let mut status = false;
124 for (i, song) in self.queue_list.list.clone().iter().enumerate() {
125 if song.contains(file) {
126 self.conn.conn.delete(i as u32).unwrap();
127 status = true;
128 }
129 }
130
131 if !status {
132 if let Some(full_path) = &self.conn.get_full_path(&file) {
133 let song = self.conn.get_song_with_only_filename(full_path);
134 self.conn.conn.push(&song)?;
135 }
136 }
137
138 if self.browser.selected != self.browser.filetree.len() - 1 {
139 self.browser.selected += 1;
140 }
141 }
142
143 SelectedTab::Queue => {
144 if self.queue_list.list.len() == 0 {
145 return Ok(());
146 }
147 let file = self
148 .queue_list
149 .list
150 .get(self.queue_list.index)
151 .unwrap()
152 .to_string();
153
154 for (i, song) in self.queue_list.list.clone().iter().enumerate() {
155 if song.contains(&file) {
156 self.conn.conn.delete(i as u32).unwrap();
157 if self.queue_list.index == self.queue_list.list.len() - 1
158 && self.queue_list.index != 0
159 {
160 self.queue_list.index -= 1;
161 }
162 }
163 }
164 }
165
166 _ => {}
167 }
168
169 self.update_queue();
170 Ok(())
171 }
172
173 pub fn cycle_tabls(&mut self) {
174 self.selected_tab = match self.selected_tab {
175 SelectedTab::Queue => SelectedTab::DirectoryBrowser,
176 SelectedTab::DirectoryBrowser => SelectedTab::Playlists,
177 SelectedTab::Playlists => SelectedTab::DirectoryBrowser,
178 };
179 }
180
181 pub fn handle_enter(&mut self) -> AppResult<()> {
182 let browser = &mut self.browser;
183 let (t, path) = browser.filetree.get(browser.selected).unwrap();
184 if t == "directory" {
185 if path != "." {
186 browser.prev_path = browser.path.clone();
187 browser.path = browser.prev_path.clone() + "/" + path;
188 browser.update_directory(&mut self.conn)?;
189 browser.prev_selected = browser.selected;
190 browser.selected = 0;
191 }
192 } else {
193 // let list = conn
194 // .songs_filenames
195 // .iter()
196 // .map(|f| f.as_str())
197 // .collect::<Vec<&str>>();
198 // let (filename, _) = rust_fuzzy_search::fuzzy_search_sorted(&path, &list)
199 // .get(0)
200 // .unwrap()
201 // .clone();
202 let index = self.queue_list.list.iter().position(|x| x.contains(path));
203
204 if index.is_some() {
205 self.conn.conn.switch(index.unwrap() as u32)?;
206 } else {
207 for filename in self.conn.songs_filenames.clone().iter() {
208 if filename.contains(path) {
209 let song = self.conn.get_song_with_only_filename(filename);
210 self.conn.push(&song)?;
211 }
212 }
213
214 // updating queue, to avoid multiple pushes of the same songs if we enter multiple times before the queue gets updated
215 self.update_queue();
216 }
217 }
218 Ok(())
219 }
220
221 pub fn search_song(&mut self) -> AppResult<()> {
222 if let Some(filename) = self.conn.get_full_path(&self.search_input) {
223 let song = self.conn.get_song_with_only_filename(&filename);
224 self.conn.push(&song)?;
225 }
226 Ok(())
227 }
228
229 // Cursor movements
230 pub fn move_cursor_left(&mut self) {
231 let cursor_moved_left = self.cursor_position.saturating_sub(1);
232 self.cursor_position = self.clamp_cursor(cursor_moved_left);
233 }
234
235 pub fn move_cursor_right(&mut self) {
236 let cursor_moved_right = self.cursor_position.saturating_add(1);
237 self.cursor_position = self.clamp_cursor(cursor_moved_right);
238 }
239
240 pub fn enter_char(&mut self, new_char: char) {
241 self.search_input.insert(self.cursor_position, new_char);
242
243 self.move_cursor_right();
244 }
245
246 pub fn delete_char(&mut self) {
247 let is_not_cursor_leftmost = self.cursor_position != 0;
248 if is_not_cursor_leftmost {
249 // Method "remove" is not used on the saved text for deleting the selected char.
250 // Reason: Using remove on String works on bytes instead of the chars.
251 // Using remove would require special care because of char boundaries.
252
253 let current_index = self.cursor_position;
254 let from_left_to_current_index = current_index - 1;
255
256 // Getting all characters before the selected character.
257 let before_char_to_delete = self.search_input.chars().take(from_left_to_current_index);
258 // Getting all characters after selected character.
259 let after_char_to_delete = self.search_input.chars().skip(current_index);
260
261 // Put all characters together except the selected one.
262 // By leaving the selected one out, it is forgotten and therefore deleted.
263 self.search_input = before_char_to_delete.chain(after_char_to_delete).collect();
264 self.move_cursor_left();
265 }
266 }
267
268 pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
269 new_cursor_pos.clamp(0, self.search_input.len())
270 }
271
272 pub fn reset_cursor(&mut self) {
273 self.cursor_position = 0;
274 }
275
276 /// Given time in seconds, convert it to hh:mm:ss
277 pub fn format_time(time: Duration) -> String {
278 let time = time.as_secs();
279 let h = time / 3600;
280 let m = (time % 3600) / 60;
281 let s = (time % 3600) % 60;
282 if h == 0 {
283 format!("{:02}:{:02}", m, s)
284 } else {
285 format!("{:02}:{:02}:{:02}", h, m, s)
286 }
287 }
288}