40 const std::map<std::string,
41 std::map<std::string, std::map<
unsigned int, std::map<unsigned int, Song>>>>&
44 : library(initial_library), INL_Thread_Manager(std::make_unique<
ThreadManager>()),
45 INL_Thread_State(INL_Thread_Manager->getThreadState()), global_keybinds(keybinds),
46 global_colors(colors), searchIndices()
54 mprisService = std::make_unique<MPRISService>(
"inLimbo");
56 INL_Thread_State.mpris_dbus_thread = std::make_unique<std::thread>(
59 GMainLoop* loop = g_main_loop_new(
nullptr, FALSE);
60 g_main_loop_run(loop);
61 g_main_loop_unref(loop);
64 INL_Thread_State.mpris_dbus_thread
67 auto screen = ScreenInteractive::Fullscreen();
68 std::atomic<bool> screen_active{
true};
70 std::thread refresh_thread(
77 using namespace std::chrono_literals;
81 std::this_thread::sleep_for(0.1s);
83 if (INL_Thread_State.is_playing)
85 current_position += 0.1;
88 if (current_position >= GetCurrentSongDuration())
95 SafePostEvent(screen, Event::Custom);
97 catch (
const std::exception& e)
99 SetDialogMessage(
"Error in refresh thread: " + std::string(e.what()));
103 SetDialogMessage(
"Unknown error occurred in refresh thread.");
112 screen.Loop(INL_Component_State.MainRenderer);
114 catch (
const std::exception& e)
116 SetDialogMessage(
"Error in UI loop: " + std::string(e.what()));
120 SetDialogMessage(
"Unknown error occurred in UI loop.");
124 screen_active =
false;
125 if (refresh_thread.joinable())
127 refresh_thread.join();
137 std::unique_ptr<MPRISService> mprisService;
143 std::shared_ptr<MiniAudioPlayer> audio_player;
144 std::unique_ptr<ThreadManager> INL_Thread_Manager;
148 std::map<std::string, std::map<std::string, std::map<unsigned int, std::map<unsigned int, Song>>>>
152 std::string current_artist;
153 bool show_dialog =
false;
154 bool is_search_active =
false;
155 bool show_audio_devices =
false;
156 std::string dialog_message;
158 bool first_g_pressed =
false;
161 std::vector<std::string> current_artist_names;
162 std::vector<std::string> current_artist_song_names;
163 std::vector<std::string> lyricLines;
164 std::vector<std::string> audioDevices;
165 std::vector<int> searchIndices;
166 std::vector<AudioDevice> audioDeviceMap;
167 std::vector<std::string> audioDeviceConf;
168 std::vector<Element> current_song_elements;
169 std::vector<unsigned int> current_inodes;
170 std::unordered_set<int> album_name_indices;
171 int albums_indices_traversed = 1;
173 std::mutex state_mutex, event_mutex;
174 std::queue<ftxui::Event> event_queue;
177 int selected_artist = 0;
178 int selected_audio_dev_line = 0;
179 int selected_inode = 1;
180 int current_lyric_line = 0;
181 std::vector<Element> lyricElements;
184 int lastVolume = volume;
185 double current_position = 0;
186 int active_screen = 0;
188 bool should_quit =
false;
189 bool focus_on_artists =
true;
190 ScreenInteractive* screen_ =
nullptr;
192 void InitializeData()
194 for (
const auto& artist_pair : library)
196 current_artist_names.push_back(artist_pair.first);
199 std::sort(current_artist_names.begin(), current_artist_names.end());
201 if (!current_artist_names.empty())
203 UpdateSongsForArtist(current_artist_names[0]);
207 for (
size_t i = 0; i < current_artist_names.size(); ++i)
208 current_search_state.ArtistSearchTrie.insert(current_artist_names[i], i);
211 void SetDialogMessage(
const std::string& message)
213 std::lock_guard<std::mutex> lock(state_mutex);
214 dialog_message = message;
218 void SafePostEvent(ScreenInteractive& screen,
const ftxui::Event& event)
221 std::lock_guard<std::mutex> lock(event_mutex);
222 event_queue.push(event);
224 screen.PostEvent(event);
227 void ProcessEvents(ScreenInteractive& screen)
229 std::lock_guard<std::mutex> lock(event_mutex);
230 while (!event_queue.empty())
239 auto getOrCreateAudioPlayer() -> std::shared_ptr<MiniAudioPlayer>
241 static std::mutex player_mutex;
244 audio_player = std::make_shared<MiniAudioPlayer>();
249 void PlayCurrentSong()
251 INL_Thread_Manager->lockPlayMutex(INL_Thread_State);
252 if (INL_Thread_State.is_processing)
256 INL_Thread_State.is_processing =
true;
259 INL_Thread_Manager->getWorkerThreadPool().enqueue(
264 audio_player = getOrCreateAudioPlayer();
265 audioDeviceMap = audio_player->enumerateDevices();
266 Song* current_song = qState.GetCurrentSongFromQueue();
269 throw std::runtime_error(
"Error: No current song found.");
272 if (file_path.empty())
274 throw std::runtime_error(
"Error: Invalid file path.");
277 if (INL_Thread_State.is_playing)
279 audio_player->stop();
282 auto load_future = audio_player->loadFileAsync(file_path,
true);
284 int loadAudioFileStatus = load_future.get();
285 if (loadAudioFileStatus == -1)
287 throw std::runtime_error(
"Error: Failed to load the audio file.");
289 INL_Thread_State.is_playing =
true;
291 audio_player->play();
292 auto durationFuture = audio_player->getDurationAsync();
293 current_playing_state.duration = durationFuture.get();
295 catch (
const std::exception& e)
297 SetDialogMessage(e.what());
298 audio_player->stop();
299 INL_Thread_State.is_playing =
false;
303 INL_Thread_State.is_processing =
false;
307 void TogglePlayback()
312 if (INL_Thread_State.is_playing)
314 audio_player->pause();
315 INL_Thread_State.is_playing =
false;
321 audio_player->resume();
326 UpdatePlayingState();
328 INL_Thread_State.is_playing =
true;
336 audio_player->setVolume(volume / 100.0f);
344 if (qState.song_queue.empty())
346 SetDialogMessage(
"Error: Queue is empty.");
347 INL_Thread_State.is_playing =
false;
351 if (qState.qIndex + 1 >= qState.song_queue.size())
353 SetDialogMessage(
"Error: No more songs in the queue.");
357 qState.incrementQIndex();
359 Song* current_song = qState.GetCurrentSongFromQueue();
362 INL_Thread_State.is_playing =
false;
363 SetDialogMessage(
"Error: Invalid song in queue.");
367 current_position = 0;
369 UpdatePlayingState();
371 catch (std::exception e)
374 dialog_message =
"Error: Invalid!!.";
381 current_position = 0;
383 UpdatePlayingState();
387 void PlayPreviousSong()
391 if (qState.song_queue.empty())
393 SetDialogMessage(
"Error: Queue is empty.");
394 INL_Thread_State.is_playing =
false;
398 if (qState.qIndex + 1 >= qState.song_queue.size())
400 SetDialogMessage(
"Error: No more previous songs in the queue.");
404 qState.decrementQIndex();
406 Song* current_song = qState.GetCurrentSongFromQueue();
409 INL_Thread_State.is_playing =
false;
410 SetDialogMessage(
"Error: Invalid song in queue.");
414 current_position = 0;
416 UpdatePlayingState();
418 catch (std::exception e)
421 dialog_message =
"Error: Invalid!!.";
426 void findAudioSinks()
428 audioDevices.clear();
429 for (
const auto& device : audioDeviceMap)
431 audioDevices.push_back(device.name);
436 auto RenderAudioConsole() -> Element
438 INL_Component_State.audioDeviceMenu =
CreateMenu(&audioDevices, &selected_audio_dev_line);
440 if (audioDevices.empty())
441 return vbox({text(
"No sinks available.") | bold | border});
446 auto audioDevComp = vbox({INL_Component_State.audioDeviceMenu->Render() | flex}) | border;
448 audioDeviceConf = audio_player->getAudioPlaybackDetails();
450 vector<Element> audioConfDetails;
451 for (
const auto& i : audioDeviceConf)
452 audioConfDetails.push_back(text(i) | frame);
454 auto finalAudioConfDetailsElement = vbox(audioConfDetails);
456 return vbox({title, separator(), audioDevComp | frame | flex, separator(),
457 finalAudioConfDetailsElement | flex | border}) |
458 flex | borderRounded;
463 void UpdateSongsForArtist(
const std::string& artist)
465 current_inodes.clear();
466 current_song_elements.clear();
467 album_name_indices.clear();
468 current_search_state.SongSearchTrie.clear();
469 current_search_state.songIndex = 0;
471 if (library.count(artist) > 0)
473 std::map<std::string, std::map<unsigned int, std::map<unsigned int, Song>>> albums;
474 for (
const auto& album_pair : library.at(artist))
476 const std::string& album_name = album_pair.first;
477 for (
const auto& disc_pair : album_pair.second)
479 for (
const auto& track_pair : disc_pair.second)
481 albums[album_name][disc_pair.first][track_pair.first] = track_pair.second;
486 for (
const auto& [album_name, discs] : albums)
488 const Song& first_song = discs.begin()->second.begin()->second;
490 global_colors.album_name_bg,
491 global_colors.album_name_fg));
492 album_name_indices.insert(current_song_elements.size() - 1);
494 for (
const auto& [disc_number, tracks] : discs)
496 for (
const auto& [track_number, song] : tracks)
499 current_inodes.push_back(song.inode);
500 current_search_state.SongSearchTrie.insert(song.metadata.title,
501 current_search_state.songIndex++);
502 current_song_elements.push_back(
503 renderSongName(disc_track_info, song.metadata.title, song.metadata.duration));
509 current_artist = artist;
511 albums_indices_traversed = 1;
514 auto GetCurrentSong(
const std::string& artist) ->
const Song&
516 const auto& artist_data = library.at(artist);
519 for (
const auto& album_pair : artist_data)
521 for (
const auto& disc_pair : album_pair.second)
523 for (
const auto& track_pair : disc_pair.second)
525 const Song& song = track_pair.second;
527 if (current_inodes[selected_inode - albums_indices_traversed] == song.
inode)
535 throw std::runtime_error(
"Song not found.");
538 void PlayThisSongNext(
const std::string& artist)
540 const Song& get_curr_song = GetCurrentSong(artist);
544 qState.insertSongToIndex(get_curr_song);
546 catch (std::exception e)
548 SetDialogMessage(
"Could not play this song next!");
551 qState.incrementQIndex();
555 void EnqueueAllSongsByArtist(
const std::string& artist,
bool clearQueue)
560 bool start_enqueue =
false;
562 if (library.find(artist) == library.end())
564 std::cerr <<
"Artist not found in the library: " << artist << std::endl;
568 const auto& artist_data = library.at(artist);
571 for (
const auto& album_pair : artist_data)
573 for (
const auto& disc_pair : album_pair.second)
575 for (
const auto& track_pair : disc_pair.second)
577 const Song& song = track_pair.second;
579 if (current_inodes[selected_inode - albums_indices_traversed] == song.
inode)
581 start_enqueue =
true;
590 void AddSongToQueue()
592 if (selected_artist >= current_artist_names.size() ||
593 selected_inode - albums_indices_traversed >= current_inodes.size())
595 throw std::runtime_error(
"Invalid artist or song selection.");
599 const Song& current_preview_song = GetCurrentSong(current_artist_names[selected_artist]);
601 qState.qPush(current_preview_song);
603 NavigateSongMenu(
true);
606 void RemoveSongFromQueue()
608 if (qState.qScreenIndex == 0)
610 SetDialogMessage(
"Unable to remove song... This is playing right now!");
613 if (qState.qScreenIndex < qState.getQueueSize())
619 auto GetCurrentSongDuration() ->
int
621 if (!current_inodes.empty() && audio_player)
623 return current_playing_state.duration;
628 void UpdatePlayingState()
631 Song* current_song = qState.GetCurrentSongFromQueue();
634 SetDialogMessage(
"Current Song not available!");
638 const auto& metadata = current_song->
metadata;
641 INL_Thread_Manager->getWorkerThreadPool().enqueue(
645 PlayingState new_state = current_playing_state;
647 if (new_state.
filePath == metadata.filePath)
656 std::lock_guard<std::mutex> lock(state_mutex);
657 current_playing_state = std::move(new_state);
660 mprisService->updateMetadata(current_playing_state.title, current_playing_state.artist,
661 current_playing_state.album,
662 static_cast<int64_t
>(current_playing_state.duration),
663 current_playing_state.comment, current_playing_state.genre,
664 current_playing_state.track, current_playing_state.discNumber);
666 current_lyric_line = 0;
670 void CreateComponents()
672 INL_Component_State.artists_list =
Scroller(
673 Renderer([&]()
mutable {
return RenderArtistMenu(current_artist_names); }), &selected_artist,
674 global_colors.artists_menu_col_bg, global_colors.inactive_menu_cursor_bg);
675 INL_Component_State.songs_list =
Scroller(
681 &selected_inode, global_colors.menu_cursor_bg, global_colors.inactive_menu_cursor_bg);
683 auto main_container =
684 Container::Horizontal({INL_Component_State.artists_list, INL_Component_State.songs_list});
690 auto last_event_time = std::chrono::steady_clock::now();
694 const int final_debounce_time = debounce_time;
695 auto debounce_duration = std::chrono::milliseconds(final_debounce_time);
697 main_container |= CatchEvent(
700 auto is_keybind_match = [&](
char key) ->
bool
702 return (event.is_character() && event.character() == std::string(1, key)) ||
703 (
event == Event::Special(std::string(1,
static_cast<char>(key))));
706 if (is_search_active)
708 if (event.is_character())
710 current_search_state.input +=
event.character();
712 current_search_state.artistIndex = 0;
713 current_search_state.songIndex = 0;
715 if (focus_on_artists)
718 current_search_state.ArtistSearchTrie.search(current_search_state.input);
719 if (!searchIndices.empty())
721 selected_artist = searchIndices[current_search_state.artistIndex];
722 UpdateSongsForArtist(current_artist_names[selected_artist]);
728 current_search_state.SongSearchTrie.search(current_search_state.input);
729 if (!searchIndices.empty())
731 selected_inode = searchIndices[current_search_state.songIndex];
736 else if (event == Event::Backspace)
738 if (!current_search_state.input.empty())
739 current_search_state.input.pop_back();
742 else if (event == Event::Return || event == Event::Escape)
744 is_search_active =
false;
745 current_search_state.input.clear();
752 if (event.is_character() && (event.character()[0] == global_keybinds.show_help ||
753 std::toupper(global_keybinds.quit_app) ||
754 event.character()[0] == global_keybinds.quit_app))
764 if (is_keybind_match(global_keybinds.goto_main_screen))
773 if (is_keybind_match(global_keybinds.remove_song_from_queue))
775 RemoveSongFromQueue();
778 else if (is_keybind_match(global_keybinds.goto_main_screen))
787 if (is_keybind_match(global_keybinds.goto_main_screen))
796 if (is_keybind_match(global_keybinds.goto_main_screen))
798 show_audio_devices =
false;
806 if (is_keybind_match(global_keybinds.play_song) && !focus_on_artists)
808 if (!current_artist.empty())
810 EnqueueAllSongsByArtist(current_artist,
true);
812 if (Song* current_song = qState.GetCurrentSongFromQueue())
815 current_position = 0;
817 UpdatePlayingState();
822 SetDialogMessage(
"No artist selected to play songs from.");
827 else if (is_keybind_match(global_keybinds.quit_app) ||
828 is_keybind_match(std::toupper(global_keybinds.quit_app)))
833 else if (is_keybind_match(global_keybinds.toggle_play))
838 else if (is_keybind_match(global_keybinds.play_song_next))
850 else if (is_keybind_match(global_keybinds.play_song_prev))
859 else if (is_keybind_match(global_keybinds.seek_ahead_5) && INL_Thread_State.is_playing)
861 seekBuffer = audio_player->seekTime(5);
862 current_position += seekBuffer;
865 else if (is_keybind_match(global_keybinds.seek_behind_5) && INL_Thread_State.is_playing)
867 if (current_position > 5)
869 seekBuffer = audio_player->seekTime(-5);
870 current_position += seekBuffer;
876 UpdatePlayingState();
879 else if (is_keybind_match(global_keybinds.replay_song))
884 else if (is_keybind_match(global_keybinds.vol_up))
886 volume = std::min(100, volume + 5);
890 else if (is_keybind_match(global_keybinds.vol_down))
892 volume = std::max(0, volume - 5);
896 else if (is_keybind_match(global_keybinds.toggle_mute))
902 else if (is_keybind_match(global_keybinds.show_help))
907 else if (is_keybind_match(global_keybinds.toggle_audio_devices))
909 show_audio_devices = !show_audio_devices;
910 if (show_audio_devices)
920 else if (is_keybind_match(global_keybinds.view_lyrics) &&
921 (current_playing_state.HasLyrics() || current_playing_state.HasComments()))
926 else if (is_keybind_match(global_keybinds.view_song_queue) && !qState.song_queue.empty())
931 else if (is_keybind_match(global_keybinds.goto_main_screen))
936 else if (is_keybind_match(global_keybinds.view_current_song_info))
941 else if (is_keybind_match(global_keybinds.toggle_focus))
943 focus_on_artists = !focus_on_artists;
944 if (focus_on_artists)
946 INL_Component_State.artists_list->TakeFocus();
950 INL_Component_State.songs_list->TakeFocus();
954 else if (is_keybind_match(global_keybinds.add_song_to_queue))
959 else if (is_keybind_match(global_keybinds.add_artists_songs_to_queue) && focus_on_artists)
961 EnqueueAllSongsByArtist(current_artist_names[selected_artist],
false);
962 if (!INL_Thread_State.is_playing)
964 current_position = 0;
966 UpdatePlayingState();
971 else if (is_keybind_match(global_keybinds.play_this_song_next) && !focus_on_artists)
973 PlayThisSongNext(current_artist_names[selected_artist]);
976 else if (is_keybind_match(global_keybinds.search_menu))
978 is_search_active =
true;
979 searchIndices.clear();
980 current_search_state.input.clear();
983 else if (is_keybind_match(global_keybinds.search_item_next))
985 if (focus_on_artists)
989 selected_artist = searchIndices[current_search_state.artistIndex];
990 UpdateSongsForArtist(current_artist_names[selected_artist]);
997 selected_inode = searchIndices[current_search_state.songIndex];
1001 else if (is_keybind_match(global_keybinds.search_item_prev))
1003 if (focus_on_artists)
1007 selected_artist = searchIndices[current_search_state.artistIndex];
1008 UpdateSongsForArtist(current_artist_names[selected_artist]);
1015 selected_inode = searchIndices[current_search_state.songIndex];
1021 if (is_keybind_match(global_keybinds.scroll_down) || event == Event::ArrowDown)
1026 else if (is_keybind_match(global_keybinds.scroll_up) || event == Event::ArrowUp)
1028 NavigateList(
false);
1032 else if (is_keybind_match(
'g'))
1035 if (!first_g_pressed)
1038 first_g_pressed =
true;
1043 NavigateListToTop(
true);
1044 first_g_pressed =
false;
1048 else if (is_keybind_match(
'G'))
1050 NavigateListToTop(
false);
1053 else if (is_keybind_match(
'x'))
1055 show_dialog =
false;
1062 INL_Component_State.MainRenderer =
1063 Renderer(main_container,
1066 int duration = GetCurrentSongDuration();
1067 float progress = duration > 0 ? (float)current_position / duration : 0;
1070 switch (active_screen)
1073 interface = RenderHelpScreen(global_keybinds);
1076 interface = RenderMainInterface(progress);
1079 interface = RenderLyricsAndInfoView();
1082 interface = RenderQueueScreen();
1085 interface = RenderAudioConsole();
1089 current_playing_state.filePath,
getCachePath(), current_playing_state.title,
1090 current_playing_state.artist, current_playing_state.album,
1091 current_playing_state.genre, current_playing_state.year,
1092 current_playing_state.track, current_playing_state.discNumber, progress);
1097 interface = dbox({interface,
RenderDialog(dialog_message) | center}) |
1100 return window(text(
" inLimbo ") | bold, vbox(interface));
1107 lyricLines = current_playing_state.formatLyrics();
1111 auto RenderLyricsAndInfoView() -> Element
1114 std::vector<Element> additionalPropertiesText;
1115 for (
const auto& [key, value] : current_playing_state.additionalProperties)
1117 if (key !=
"LYRICS")
1119 additionalPropertiesText.push_back(hbox({text(key +
": "), text(value) | dim}));
1123 INL_Component_State.lyrics_scroller =
CreateMenu(&lyricLines, ¤t_lyric_line);
1127 std::string end_text =
"Use arrow keys to scroll, Press '" +
1128 charToStr(global_keybinds.goto_main_screen) +
"' to go back home.";
1130 auto lyrics_pane = vbox({
1131 INL_Component_State.lyrics_scroller->Render() | flex,
1134 auto info_pane = window(text(
" Additional Info ") | bold | center,
1135 vbox(additionalPropertiesText) | frame | flex);
1138 lyrics_pane | frame | flex | border,
1140 text(end_text) | dim | center | border,
1143 vbox({info_pane}) | flex}) |
1147 auto RenderQueueScreen() -> Element
1149 INL_Component_State.songs_queue_comp =
1150 CreateMenu(&qState.song_queue_names, &qState.qScreenIndex);
1151 qState.UpdateSongQueueList();
1154 underlined | center;
1156 auto separator_line = separator() | dim | flex;
1158 std::string end_text =
"Use '" +
charToStr(global_keybinds.remove_song_from_queue) +
1159 "' to remove selected song from queue, Press '" +
1160 charToStr(global_keybinds.goto_main_screen) +
"' to go back home.";
1162 auto queue_container = vbox({
1163 INL_Component_State.songs_queue_comp->Render() |
1164 color(global_colors.song_queue_menu_fg) | flex,
1166 border | color(global_colors.song_queue_menu_bor_col);
1170 queue_container | frame | flex,
1172 text(end_text) | dim | center,
1177 void NavigateSongMenu(
bool move_down)
1179 if (current_song_elements.empty())
1182 int initial_inode = selected_inode;
1187 if (album_name_indices.count(selected_inode))
1189 move_down ? ++albums_indices_traversed : --albums_indices_traversed;
1192 if (selected_inode == 0 && move_down)
1194 albums_indices_traversed = 1;
1196 else if (selected_inode == current_song_elements.size() - 1 && !move_down)
1198 albums_indices_traversed = album_name_indices.size();
1201 if (selected_inode == initial_inode)
1203 }
while (album_name_indices.count(selected_inode));
1206 void NavigateList(
bool move_down)
1208 switch (active_screen)
1211 if (focus_on_artists && !current_artist_names.empty())
1214 UpdateSongsForArtist(current_artist_names[selected_artist]);
1216 else if (!current_inodes.empty())
1218 NavigateSongMenu(move_down);
1235 void NavigateListToTop(
bool move_up)
1237 switch (active_screen)
1240 if (focus_on_artists && !current_artist_names.empty())
1242 selected_artist = move_up ? 0 : current_artist_names.size() - 1;
1243 UpdateSongsForArtist(current_artist_names[selected_artist]);
1245 albums_indices_traversed = 1;
1247 else if (!current_inodes.empty())
1249 selected_inode = move_up ? 1 : current_song_elements.size() - 1;
1250 albums_indices_traversed = move_up ? 1 : album_name_indices.size();
1255 qState.qScreenIndex = move_up ? 0 : qState.song_queue_names.size() - 1;
1259 current_lyric_line = move_up ? 0 : lyricLines.size() - 1;
1264 auto GetCurrWinColor(
bool focused) ->
Color
1266 return focused ? global_colors.active_win_border_color
1267 : global_colors.inactive_win_border_color;
1270 auto RenderProgressBar(
float progress) -> Element
1272 auto progress_style = INL_Thread_State.is_playing
1273 ? color(global_colors.progress_bar_playing_col)
1274 : color(global_colors.progress_bar_not_playing_col);
1277 text(
FormatTime((
int)current_position)) | progress_style,
1278 gauge(progress) | flex | progress_style,
1279 text(
FormatTime(GetCurrentSongDuration())) | progress_style,
1283 auto RenderQueueBar() -> Element
1285 std::string queue_info =
" ";
1286 int songs_left = qState.getQueueSize() - qState.qIndex - 1;
1287 if (songs_left >= qState.getQueueSize() && songs_left < 0)
1289 queue_info += std::to_string(songs_left) +
" songs left.";
1291 std::string up_next_song =
" Next up: ";
1292 if (qState.getQueueSize() > 1 && songs_left > 0)
1293 up_next_song += qState.song_queue[qState.qIndex + 1].metadata.title +
" by " +
1294 qState.song_queue[qState.qIndex + 1].metadata.artist;
1296 up_next_song +=
"Next song not available.";
1299 text(queue_info) | dim | border | bold,
1304 auto RenderMainInterface(
float progress) -> Element
1306 std::string current_song_info;
1307 std::string year_info;
1308 std::string additional_info;
1310 if (!current_playing_state.artist.empty())
1313 year_info = std::to_string(current_playing_state.year) +
" ";
1315 current_playing_state.genre, current_playing_state.has_comment,
1316 current_playing_state.has_lyrics, global_props.show_bitrate, current_playing_state.bitrate);
1319 std::string status =
1322 auto left_pane = vbox({
1323 text(
" Artists") | bold | color(global_colors.artists_title_bg) | inverted,
1325 INL_Component_State.artists_list->Render() | frame | flex |
1328 borderHeavy | color(GetCurrWinColor(focus_on_artists));
1330 auto right_pane = vbox({
1331 text(
" Songs") | bold | color(global_colors.songs_title_bg) | inverted,
1333 INL_Component_State.songs_list->Render() | frame | flex |
1336 borderHeavy | color(GetCurrWinColor(!focus_on_artists));
1338 auto panes = vbox({hbox({
1339 left_pane | size(WIDTH, EQUAL, 100) | size(HEIGHT, EQUAL, 100) | flex,
1340 right_pane | size(WIDTH, EQUAL, 100) | size(HEIGHT, EQUAL, 100) | flex,
1345 auto progress_bar = RenderProgressBar(progress);
1346 auto volume_bar =
RenderVolumeBar(volume, global_colors.volume_bar_col);
1347 auto queue_bar = RenderQueueBar();
1348 auto status_bar =
RenderStatusBar(status, current_song_info, additional_info, year_info,
1349 global_colors, current_playing_state.artist);
1351 is_search_active ==
true ?
RenderSearchBar(current_search_state.input) : filler();
1355 progress_bar | border | flex_grow,
1356 volume_bar | border,
1359 status_bar, search_bar}) |
1368 INL_Thread_Manager->lockPlayMutex(INL_Thread_State);
1371 audio_player->stop();
1375 if (INL_Thread_State.play_future.valid())
1377 auto status = INL_Thread_State.play_future.wait_for(std::chrono::milliseconds(50));
1378 if (status != std::future_status::ready)
1381 SetDialogMessage(
"Warning: Audio shutdown timed out");
1387 screen_->ExitLoopClosure()();