Krolyxon
Commit 255687b

rmptuisrcapp.rs

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