Krolyxon
Commit e500c52

rmptuisrcapp.rs

172 lines · 5.3 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
27#[derive(Debug, PartialEq, Clone)]
28pub enum SelectedTab {
29 DirectoryBrowser,
30 Queue,
31 Playlists,
32}
33
34impl App {
35 pub fn builder(addrs: &str) -> AppResult<Self> {
36 let mut conn = Connection::new(addrs).unwrap();
37 let mut queue_list = ContentList::new();
38 let mut pl_list = ContentList::new();
39 pl_list.list = Self::get_playlist(&mut conn.conn)?;
40
41 Self::get_queue(&mut conn, &mut queue_list.list);
42
43 let browser = FileBrowser::new();
44 Ok(Self {
45 running: true,
46 conn,
47 queue_list,
48 pl_list,
49 selected_tab: SelectedTab::DirectoryBrowser,
50 browser,
51 inputmode: InputMode::Normal,
52 search_input: String::new(),
53 cursor_position: 0,
54 })
55 }
56
57 pub fn tick(&mut self) {
58 self.conn.update_status();
59 self.update_queue();
60 // self.browser.update_directory(&mut self.conn).unwrap();
61 }
62
63 pub fn quit(&mut self) {
64 self.running = false;
65 }
66
67 pub fn get_queue(conn: &mut Connection, vec: &mut Vec<String>) {
68 // conn.conn.queue().unwrap().into_iter().for_each(|x| {
69 // if let Some(title) = x.title {
70 // if let Some(artist) = x.artist {
71 // vec.push(format!("{} - {}", artist, title));
72 // } else {
73 // vec.push(title)
74 // }
75 // } else {
76 // vec.push(x.file)
77 // }
78 // });
79 conn.conn.queue().unwrap().into_iter().for_each(|x| {
80 vec.push(x.file);
81 });
82 }
83
84 pub fn update_queue(&mut self) {
85 self.queue_list.list.clear();
86 Self::get_queue(&mut self.conn, &mut self.queue_list.list);
87 }
88
89 pub fn get_playlist(conn: &mut Client) -> AppResult<Vec<String>> {
90 let list: Vec<String> = conn.playlists()?.iter().map(|p| p.clone().name).collect();
91 Ok(list)
92 }
93
94 pub fn update_playlist(&mut self) -> AppResult<()> {
95 Self::get_playlist(&mut self.conn.conn)?;
96 Ok(())
97 }
98
99 pub fn cycle_tabls(&mut self) {
100 self.selected_tab = match self.selected_tab {
101 SelectedTab::DirectoryBrowser => SelectedTab::Queue,
102 SelectedTab::Queue => SelectedTab::Playlists,
103 SelectedTab::Playlists => SelectedTab::DirectoryBrowser,
104 };
105 }
106
107 pub fn search_song(&mut self) -> AppResult<()> {
108 let list = self
109 .conn
110 .songs_filenames
111 .iter()
112 .map(|f| f.as_str())
113 .collect::<Vec<&str>>();
114 let (filename, _) =
115 rust_fuzzy_search::fuzzy_search_sorted(self.search_input.as_str(), &list)
116 .get(0)
117 .unwrap()
118 .clone();
119
120 let song = self.conn.get_song_with_only_filename(filename);
121 self.conn.push(&song)?;
122
123 Ok(())
124 }
125
126 // Cursor movements
127 pub fn move_cursor_left(&mut self) {
128 let cursor_moved_left = self.cursor_position.saturating_sub(1);
129 self.cursor_position = self.clamp_cursor(cursor_moved_left);
130 }
131
132 pub fn move_cursor_right(&mut self) {
133 let cursor_moved_right = self.cursor_position.saturating_add(1);
134 self.cursor_position = self.clamp_cursor(cursor_moved_right);
135 }
136
137 pub fn enter_char(&mut self, new_char: char) {
138 self.search_input.insert(self.cursor_position, new_char);
139
140 self.move_cursor_right();
141 }
142
143 pub fn delete_char(&mut self) {
144 let is_not_cursor_leftmost = self.cursor_position != 0;
145 if is_not_cursor_leftmost {
146 // Method "remove" is not used on the saved text for deleting the selected char.
147 // Reason: Using remove on String works on bytes instead of the chars.
148 // Using remove would require special care because of char boundaries.
149
150 let current_index = self.cursor_position;
151 let from_left_to_current_index = current_index - 1;
152
153 // Getting all characters before the selected character.
154 let before_char_to_delete = self.search_input.chars().take(from_left_to_current_index);
155 // Getting all characters after selected character.
156 let after_char_to_delete = self.search_input.chars().skip(current_index);
157
158 // Put all characters together except the selected one.
159 // By leaving the selected one out, it is forgotten and therefore deleted.
160 self.search_input = before_char_to_delete.chain(after_char_to_delete).collect();
161 self.move_cursor_left();
162 }
163 }
164
165 pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
166 new_cursor_pos.clamp(0, self.search_input.len())
167 }
168
169 pub fn reset_cursor(&mut self) {
170 self.cursor_position = 0;
171 }
172}