Krolyxon
Commit bf939a4

rmptuisrcapp.rs

248 lines · 8.0 KB · Rust

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