47 const std::map<std::string,
48 std::map<std::string, std::map<
unsigned int, std::map<unsigned int, Song>>>>&
50 : library(initial_library), INL_Thread_Manager(std::make_unique<
ThreadManager>()),
51 INL_Thread_State(INL_Thread_Manager->getThreadState())
59 mprisService = std::make_unique<MPRISService>(
"inLimbo");
61 std::thread mpris_dbus_thread(
64 GMainLoop* loop = g_main_loop_new(
nullptr, FALSE);
65 g_main_loop_run(loop);
71 auto screen = ScreenInteractive::Fullscreen();
75 std::thread refresh_thread(
82 using namespace std::chrono_literals;
87 std::this_thread::sleep_for(0.1s);
89 if (INL_Thread_State.is_playing)
91 current_position += 0.1;
92 if (current_position >= GetCurrentSongDuration())
100 screen.PostEvent(Event::Custom);
103 catch (
const std::exception& e)
106 dialog_message =
"Error in refresh thread: " + std::string(e.what());
111 dialog_message =
"Unknown error occurred in refresh thread.";
115 refresh_thread.detach();
117 catch (
const std::exception& e)
119 dialog_message =
"Error starting refresh thread: " + std::string(e.what());
124 dialog_message =
"Unknown error occurred while starting refresh thread.";
132 screen.Loop(INL_Component_State.MainRenderer);
134 catch (
const std::exception& e)
136 dialog_message =
"Error in UI loop: " + std::string(e.what());
141 dialog_message =
"Unknown error occurred in UI loop.";
153 bool has_comment =
false;
154 bool has_lyrics =
false;
156 unsigned int year = 0;
157 unsigned int track = 0;
158 unsigned int discNumber = 0;
161 std::unordered_map<std::string, std::string> additionalProperties;
162 std::string filePath;
165 PlayingState current_playing_state;
167 std::unique_ptr<MPRISService> mprisService;
172 std::unique_ptr<MiniAudioPlayer> audio_player;
173 std::unique_ptr<ThreadManager> INL_Thread_Manager;
174 ThreadManager::ThreadState& INL_Thread_State;
176 std::vector<Song> song_queue;
177 int current_song_queue_index = 0;
180 std::map<std::string, std::map<std::string, std::map<unsigned int, std::map<unsigned int, Song>>>>
184 std::string current_artist;
185 unsigned int current_disc = 1;
186 unsigned int current_track = 1;
187 bool show_dialog =
false;
188 std::string dialog_message;
190 bool first_g_pressed =
false;
193 std::vector<std::string> current_artist_names;
194 std::vector<std::string> song_queue_names;
195 std::vector<std::string> lyricLines;
196 std::vector<Element> current_song_elements;
197 std::vector<unsigned int> current_inodes;
198 std::unordered_set<int> album_name_indices;
199 int albums_indices_traversed = 1;
202 int selected_artist = 0;
203 int selected_song_queue = 0;
204 int selected_inode = 1;
205 int current_lyric_line = 0;
206 std::vector<Element> lyricElements;
209 int lastVolume = volume;
210 double current_position = 0;
213 bool should_quit =
false;
214 bool focus_on_artists =
true;
215 ScreenInteractive* screen_ =
nullptr;
218 ComponentState INL_Component_State;
220 void InitializeData()
223 for (
const auto& artist_pair : library)
225 current_artist_names.push_back(artist_pair.first);
229 std::sort(current_artist_names.begin(), current_artist_names.end());
232 if (!current_artist_names.empty())
234 UpdateSongsForArtist(current_artist_names[0]);
240 void PlayCurrentSong()
242 INL_Thread_Manager->lockPlayMutex(INL_Thread_State);
243 if (INL_Thread_State.is_processing)
245 INL_Thread_Manager->unlockPlayMutex(INL_Thread_State);
248 INL_Thread_State.is_processing =
true;
250 INL_Thread_State.play_future =
251 std::async(std::launch::async,
258 audio_player = std::make_unique<MiniAudioPlayer>();
261 Song* current_song = GetCurrentSongFromQueue();
264 throw std::runtime_error(
"Error: No current song found.");
268 if (file_path.empty())
270 throw std::runtime_error(
"Error: Invalid file path.");
274 if (INL_Thread_State.is_playing)
276 audio_player->stop();
277 INL_Thread_State.is_playing =
false;
281 int loadAudioFileStatus = audio_player->loadFile(file_path);
282 if (loadAudioFileStatus == -1)
284 throw std::runtime_error(
"Error: Failed to load the audio file.");
287 INL_Thread_State.is_playing =
true;
289 audio_player->play();
290 current_playing_state.duration = audio_player->getDuration();
292 catch (
const std::exception& e)
295 dialog_message = e.what();
296 INL_Thread_State.is_playing =
false;
299 INL_Thread_Manager->lockPlayMutex(INL_Thread_State);
300 INL_Thread_State.is_processing =
false;
301 INL_Thread_Manager->unlockPlayMutex(INL_Thread_State);
304 INL_Thread_Manager->unlockPlayMutex(INL_Thread_State);
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);
342 if (current_song_queue_index + 1 < song_queue.size())
344 current_song_queue_index++;
345 if (Song* current_song = GetCurrentSongFromQueue())
347 current_position = 0;
349 UpdatePlayingState();
355 dialog_message =
"Error: No more songs in the queue.";
361 current_position = 0;
363 UpdatePlayingState();
367 void PlayPreviousSong()
370 if (current_position > 3.0)
376 if (current_song_queue_index > 0)
378 current_song_queue_index--;
379 current_position = 0;
381 UpdatePlayingState();
386 dialog_message =
"Error: No previous song available.";
392 void UpdateSongsForArtist(
const std::string& artist)
394 current_inodes.clear();
395 current_song_elements.clear();
396 album_name_indices.clear();
398 if (library.count(artist) > 0)
401 std::map<std::string, std::map<unsigned int, std::map<unsigned int, Song>>> albums;
402 for (
const auto& album_pair : library.at(artist))
404 const std::string& album_name = album_pair.first;
405 for (
const auto& disc_pair : album_pair.second)
407 for (
const auto& track_pair : disc_pair.second)
409 albums[album_name][disc_pair.first][track_pair.first] = track_pair.second;
415 for (
const auto& [album_name, discs] : albums)
418 const Song& first_song = discs.begin()->second.begin()->second;
419 current_song_elements.push_back(
421 album_name_indices.insert(current_song_elements.size() - 1);
423 for (
const auto& [disc_number, tracks] : discs)
425 for (
const auto& [track_number, song] : tracks)
427 std::string disc_track_info =
" " +
428 std::to_string(disc_number) +
"-" + std::to_string(track_number) +
" ";
429 current_inodes.push_back(song.inode);
430 current_song_elements.push_back(
431 renderSongName(disc_track_info, song.metadata.title, song.metadata.duration));
437 current_artist = artist;
443 current_song_queue_index = 0;
446 const Song& GetCurrentSong(
const std::string& artist)
448 const auto& artist_data = library.at(artist);
451 for (
const auto& album_pair : artist_data)
453 for (
const auto& disc_pair : album_pair.second)
455 for (
const auto& track_pair : disc_pair.second)
457 const Song& song = track_pair.second;
459 if (current_inodes[selected_inode - albums_indices_traversed] == song.
inode)
467 throw std::runtime_error(
"Song not found.");
470 void PlayThisSongNext(
const std::string& artist)
472 const Song& get_curr_song = GetCurrentSong(artist);
476 song_queue.insert(song_queue.begin() + current_song_queue_index + 1, get_curr_song);
478 catch (std::exception e)
480 dialog_message =
"Could not play this song next!";
484 current_song_queue_index++;
488 void EnqueueAllSongsByArtist(
const std::string& artist,
bool clearQueue)
494 bool start_enqueue =
false;
497 if (library.find(artist) == library.end())
499 std::cerr <<
"Artist not found in the library: " << artist << std::endl;
503 const auto& artist_data = library.at(artist);
506 for (
const auto& album_pair : artist_data)
508 for (
const auto& disc_pair : album_pair.second)
510 for (
const auto& track_pair : disc_pair.second)
512 const Song& song = track_pair.second;
514 if (current_inodes[selected_inode - albums_indices_traversed] == song.
inode)
516 start_enqueue =
true;
520 song_queue.push_back(song);
526 void AddSongToQueue()
529 if (selected_artist >= current_artist_names.size() ||
530 selected_inode - albums_indices_traversed >= current_inodes.size())
532 throw std::runtime_error(
"Invalid artist or song selection.");
536 const Song& current_preview_song = GetCurrentSong(current_artist_names[selected_artist]);
539 song_queue.push_back(current_preview_song);
541 NavigateSongMenu(
true);
544 void RemoveSongFromQueue()
546 if (selected_song_queue == 0)
548 dialog_message =
"Unable to remove song... This is playing right now!";
552 if (selected_song_queue < song_queue.size())
554 song_queue.erase(song_queue.begin() + selected_song_queue);
558 Song* GetCurrentSongFromQueue()
560 if (!song_queue.empty() && current_song_queue_index < song_queue.size())
562 return &song_queue[current_song_queue_index];
565 dialog_message =
"Something went worng";
570 int GetCurrentSongDuration()
572 if (!current_inodes.empty() && audio_player)
574 return current_playing_state.duration;
579 void UpdatePlayingState()
581 if (Song* current_song = GetCurrentSongFromQueue())
583 const auto& metadata = current_song->
metadata;
585 current_playing_state.
artist = metadata.artist;
586 current_playing_state.title = metadata.title;
587 current_playing_state.album = metadata.album;
588 current_playing_state.genre = metadata.genre;
589 current_playing_state.comment = metadata.comment;
590 current_playing_state.year = metadata.year;
591 current_playing_state.track = metadata.track;
592 current_playing_state.discNumber = metadata.discNumber;
593 current_playing_state.lyrics = metadata.lyrics;
594 current_playing_state.has_comment = (metadata.comment !=
"No Comment");
595 current_playing_state.has_lyrics = (metadata.lyrics !=
"No Lyrics");
596 current_playing_state.filePath = metadata.filePath;
600 for (
const auto& [key, value] : metadata.additionalProperties)
602 current_playing_state.additionalProperties[key] = value;
606 const std::string& mprisSongTitle = current_playing_state.title;
607 const std::string& mprisSongArtist = current_playing_state.artist;
608 const std::string& mprisSongAlbum = current_playing_state.album;
609 const std::string& mprisSongComment = current_playing_state.comment;
610 const std::string& mprisSongGenre = current_playing_state.genre;
612 mprisService->updateMetadata(mprisSongTitle, mprisSongArtist, mprisSongAlbum,
613 static_cast<int64_t
>(GetCurrentSongFromQueue()->metadata.duration),
614 mprisSongComment, mprisSongGenre, current_playing_state.track,
615 current_playing_state.discNumber);
617 current_lyric_line = 0;
620 void CreateComponents()
622 MenuOption artist_menu_options;
623 artist_menu_options.on_change = [&]()
625 if (focus_on_artists && selected_artist < current_artist_names.size())
627 UpdateSongsForArtist(current_artist_names[selected_artist]);
630 artist_menu_options.focused_entry = &selected_artist;
632 MenuOption song_menu_options;
633 song_menu_options.on_change = [&]() {};
634 song_menu_options.focused_entry = &selected_inode;
636 INL_Component_State.artists_list =
637 Menu(¤t_artist_names, &selected_artist, artist_menu_options);
638 INL_Component_State.songs_list =
644 &selected_inode, global_colors.menu_cursor_bg);
646 auto main_container =
647 Container::Horizontal({INL_Component_State.artists_list, INL_Component_State.songs_list});
652 auto last_event_time = std::chrono::steady_clock::now();
656 const int final_debounce_time = debounce_time;
657 auto debounce_duration = std::chrono::milliseconds(final_debounce_time);
659 main_container |= CatchEvent(
662 auto is_keybind_match = [&](
char key) ->
bool
664 return (event.is_character() && event.character() == std::string(1, key)) ||
665 (
event == Event::Special(std::string(1,
static_cast<char>(key))));
670 if (event.is_character() && (event.character()[0] == global_keybinds.show_help ||
671 std::toupper(global_keybinds.quit_app) ||
672 event.character()[0] == global_keybinds.quit_app))
682 if (is_keybind_match(global_keybinds.goto_main_screen))
688 else if (is_keybind_match(global_keybinds.scroll_down))
693 else if (is_keybind_match(global_keybinds.scroll_up))
698 else if (is_keybind_match(
'g'))
701 if (!first_g_pressed)
704 first_g_pressed =
true;
709 NavigateListToTop(
true);
710 first_g_pressed =
false;
714 else if (is_keybind_match(
'G'))
716 NavigateListToTop(
false);
723 if (is_keybind_match(global_keybinds.remove_song_from_queue))
725 RemoveSongFromQueue();
728 else if (is_keybind_match(global_keybinds.scroll_down))
733 else if (is_keybind_match(global_keybinds.scroll_up))
738 else if (is_keybind_match(
'g'))
741 if (!first_g_pressed)
744 first_g_pressed =
true;
749 NavigateListToTop(
true);
750 first_g_pressed =
false;
754 else if (is_keybind_match(
'G'))
756 NavigateListToTop(
false);
759 else if (is_keybind_match(global_keybinds.goto_main_screen))
764 else if (is_keybind_match(
'x'))
771 else if (event.is_mouse())
776 if (is_keybind_match(global_keybinds.goto_main_screen))
786 if (is_keybind_match(global_keybinds.play_song) && !focus_on_artists)
788 if (!current_artist.empty())
790 EnqueueAllSongsByArtist(current_artist,
true);
792 if (Song* current_song = GetCurrentSongFromQueue())
795 current_position = 0;
797 UpdatePlayingState();
803 dialog_message =
"No artist selected to play songs from.";
808 else if (is_keybind_match(global_keybinds.quit_app) ||
809 is_keybind_match(std::toupper(global_keybinds.quit_app)))
814 else if (is_keybind_match(global_keybinds.toggle_play))
819 else if (is_keybind_match(global_keybinds.play_song_next))
821 auto now = std::chrono::steady_clock::now();
822 if (now - last_event_time < debounce_duration)
824 audio_player->stop();
828 last_event_time = now;
831 else if (is_keybind_match(global_keybinds.play_song_prev))
833 auto now = std::chrono::steady_clock::now();
834 if (now - last_event_time < debounce_duration)
836 last_event_time = now;
840 else if (is_keybind_match(global_keybinds.seek_ahead_5))
842 seekBuffer = audio_player->seekTime(5);
843 current_position += seekBuffer;
845 else if (is_keybind_match(global_keybinds.seek_behind_5))
847 if (current_position > 5)
849 seekBuffer = audio_player->seekTime(-5);
850 current_position += seekBuffer;
856 UpdatePlayingState();
858 else if (is_keybind_match(global_keybinds.replay_song))
863 else if (is_keybind_match(global_keybinds.vol_up))
865 volume = std::min(100, volume + 5);
869 else if (is_keybind_match(global_keybinds.vol_down))
871 volume = std::max(0, volume - 5);
875 else if (is_keybind_match(global_keybinds.toggle_mute))
890 else if (is_keybind_match(global_keybinds.show_help))
895 else if (is_keybind_match(global_keybinds.scroll_down))
900 else if (is_keybind_match(global_keybinds.scroll_up))
905 else if (is_keybind_match(
'x'))
910 else if (is_keybind_match(global_keybinds.view_lyrics) &&
911 (current_playing_state.has_lyrics || current_playing_state.has_comment))
916 else if (is_keybind_match(global_keybinds.view_song_queue) && !song_queue.empty())
921 else if (is_keybind_match(global_keybinds.goto_main_screen))
926 else if (is_keybind_match(global_keybinds.view_current_song_info))
932 else if (is_keybind_match(
'g'))
935 if (!first_g_pressed)
938 first_g_pressed =
true;
943 NavigateListToTop(
true);
944 first_g_pressed =
false;
948 else if (is_keybind_match(
'G'))
950 NavigateListToTop(
false);
953 else if (is_keybind_match(global_keybinds.toggle_focus))
955 focus_on_artists = !focus_on_artists;
956 if (focus_on_artists)
958 INL_Component_State.artists_list->TakeFocus();
962 INL_Component_State.songs_list->TakeFocus();
966 else if (is_keybind_match(global_keybinds.add_song_to_queue))
971 else if (is_keybind_match(global_keybinds.add_artists_songs_to_queue) && focus_on_artists)
976 EnqueueAllSongsByArtist(current_artist_names[selected_artist],
false);
980 else if (is_keybind_match(global_keybinds.play_this_song_next) && !focus_on_artists)
982 PlayThisSongNext(current_artist_names[selected_artist]);
1001 INL_Component_State.MainRenderer =
1002 Renderer(main_container,
1005 int duration = GetCurrentSongDuration();
1006 float progress = duration > 0 ? (float)current_position / duration : 0;
1011 interface = RenderHelpScreen();
1015 interface = RenderMainInterface(progress);
1020 interface = RenderLyricsAndInfoView();
1024 interface = RenderQueueScreen();
1029 current_playing_state.filePath,
getCachePath(), current_playing_state.title,
1030 current_playing_state.artist, current_playing_state.album,
1031 current_playing_state.genre, current_playing_state.year,
1032 current_playing_state.track, current_playing_state.discNumber, progress);
1040 RenderDialog() | center
1045 return vbox(interface);
1049 Element RenderDialog()
1056 separator() | color(Color::GrayLight),
1060 size(WIDTH, LESS_THAN, 60) | size(HEIGHT, LESS_THAN, 8) |
1067 lyricLines =
formatLyrics(current_playing_state.lyrics);
1071 Element RenderLyricsAndInfoView()
1074 std::vector<Element> additionalPropertiesText;
1075 for (
const auto& [key, value] : current_playing_state.additionalProperties)
1077 if (key !=
"LYRICS")
1079 additionalPropertiesText.push_back(hbox({text(key +
": "), text(value) | dim}));
1083 INL_Component_State.lyrics_scroller =
CreateMenu(&lyricLines, ¤t_lyric_line);
1087 std::string end_text =
"Use arrow keys to scroll, Press '" +
1088 std::string(1,
static_cast<char>(global_keybinds.goto_main_screen)) +
1089 "' to go back home.";
1091 auto lyrics_pane = vbox({
1092 INL_Component_State.lyrics_scroller->Render() | flex,
1095 auto info_pane = window(text(
" Additional Info ") | bold | center | inverted,
1096 vbox(additionalPropertiesText) | frame | flex);
1099 lyrics_pane | frame | flex | border,
1101 text(end_text) | dim | center | border,
1104 vbox({info_pane}) | flex}) |
1108 void UpdateSongQueueList()
1110 song_queue_names.clear();
1112 for (
long unsigned int i = current_song_queue_index; i < song_queue.size(); i++)
1114 std::string eleName = song_queue[i].metadata.title +
" by " + song_queue[i].metadata.artist;
1115 if (i == current_song_queue_index)
1117 song_queue_names.push_back(eleName);
1123 Element RenderQueueScreen()
1125 INL_Component_State.songs_queue_comp =
CreateMenu(&song_queue_names, &selected_song_queue);
1126 UpdateSongQueueList();
1129 underlined | center;
1131 auto separator_line = separator() | dim | flex;
1133 std::string end_text =
"Use '" +
charToStr(global_keybinds.remove_song_from_queue) +
1134 "' to remove selected song from queue, Press '" +
1135 charToStr(global_keybinds.goto_main_screen) +
"' to go back home.";
1137 auto queue_container = vbox({
1138 INL_Component_State.songs_queue_comp->Render() |
1139 color(global_colors.song_queue_menu_fg) | flex,
1141 border | color(global_colors.song_queue_menu_bor_col);
1145 queue_container | frame | flex,
1147 text(end_text) | dim | center,
1152 void NavigateSongMenu(
bool move_down)
1154 int initial_inode = selected_inode;
1159 selected_inode = (selected_inode + 1) % current_song_elements.size();
1164 (selected_inode - 1 + current_song_elements.size()) % current_song_elements.size();
1166 if (album_name_indices.find(selected_inode) != album_name_indices.end())
1169 albums_indices_traversed++;
1171 albums_indices_traversed--;
1173 if (selected_inode == 0 && move_down)
1175 albums_indices_traversed = 1;
1178 if (selected_inode == initial_inode)
1182 }
while (album_name_indices.find(selected_inode) !=
1183 album_name_indices.end());
1186 void NavigateList(
bool move_down)
1190 if (focus_on_artists)
1192 if (!current_artist_names.empty())
1196 selected_artist = (selected_artist + 1) % current_artist_names.size();
1201 (selected_artist - 1 + current_artist_names.size()) % current_artist_names.size();
1203 UpdateSongsForArtist(current_artist_names[selected_artist]);
1205 albums_indices_traversed = 1;
1210 if (!current_inodes.empty())
1212 NavigateSongMenu(move_down);
1220 selected_song_queue = (selected_song_queue + 1) % song_queue_names.size();
1224 selected_song_queue =
1225 (selected_song_queue - 1 + song_queue_names.size()) % song_queue_names.size();
1232 current_lyric_line = (current_lyric_line + 1) % lyricLines.size();
1236 current_lyric_line = (current_lyric_line - 1 + lyricLines.size()) % lyricLines.size();
1241 void NavigateListToTop(
bool move_up)
1245 if (focus_on_artists)
1247 if (!current_artist_names.empty())
1252 selected_artist = 0;
1256 selected_artist = current_artist_names.size() - 1;
1262 if (!current_inodes.empty())
1268 albums_indices_traversed = 1;
1273 selected_inode = current_song_elements.size() - 1;
1274 albums_indices_traversed = album_name_indices.size();
1277 if (selected_inode < 0 || selected_inode >= current_song_elements.size())
1280 albums_indices_traversed = 1;
1289 selected_song_queue = 0;
1293 selected_song_queue = song_queue_names.size() - 1;
1300 current_lyric_line = 0;
1304 current_lyric_line = lyricLines.size() - 1;
1309 Element RenderHelpScreen()
1315 [&](
const std::string& key,
const std::string& description,
TrueColors::Color color)
1324 auto controls_list =
1326 createRow(
charToStr(global_keybinds.toggle_play),
"Toggle playback",
1328 createRow(
charToStr(global_keybinds.play_song_next),
"Next song",
1330 createRow(
charToStr(global_keybinds.play_song_prev),
"Previous song",
1334 createRow(
charToStr(global_keybinds.vol_down),
"Volume down",
1336 createRow(
charToStr(global_keybinds.toggle_mute),
1338 createRow(
charToStr(global_keybinds.toggle_focus),
"Switch focus",
1342 createRow(
charToStr(global_keybinds.seek_ahead_5),
"Seek ahead by 5s",
1344 createRow(
charToStr(global_keybinds.seek_behind_5),
"Seek behind by 5s",
1346 createRow(
charToStr(global_keybinds.replay_song),
"Replay current song",
1349 createRow(
charToStr(global_keybinds.show_help),
"Toggle this help",
1351 createRow(
charToStr(global_keybinds.add_song_to_queue),
"Add song to queue",
1353 createRow(
charToStr(global_keybinds.add_song_to_queue),
"Remove song from queue",
1355 createRow(
charToStr(global_keybinds.goto_main_screen),
"Go to song tree view",
1360 auto symbols_explanation = vbox({
1361 hbox({text(
LYRICS_AVAIL), text(
" -> "), text(
"The current song has lyrics metadata.")}) |
1364 text(
"The current song has additional properties metadata.")}) |
1368 std::string footer_text =
1369 "Press '" +
charToStr(global_keybinds.show_help) +
"' to return to inLimbo.";
1374 controls_list | border | flex,
1376 symbols_explanation | border | flex,
1382 Color GetCurrWinColor(
bool focused)
1384 return focused ? global_colors.active_win_border_color
1385 : global_colors.inactive_win_border_color;
1388 Element RenderMainInterface(
float progress)
1390 std::string current_song_info;
1391 std::string year_info;
1392 std::string additional_info;
1394 if (!current_playing_state.artist.empty())
1396 current_song_info =
" - " + current_playing_state.title;
1397 year_info = std::to_string(current_playing_state.year) +
" ";
1399 if (current_playing_state.genre !=
"Unknown Genre")
1401 additional_info +=
"Genre: " + current_playing_state.genre +
STATUS_BAR_DELIM;
1403 if (current_playing_state.has_comment)
1407 if (current_playing_state.has_lyrics)
1415 std::string status =
1418 auto left_pane = vbox({
1419 text(
" Artists") | bold | color(global_colors.artists_title_bg) | inverted,
1421 INL_Component_State.artists_list->Render() | frame | flex |
1424 borderHeavy | color(GetCurrWinColor(focus_on_artists));
1426 auto right_pane = vbox({
1427 text(
" Songs") | bold | color(global_colors.songs_title_bg) | inverted,
1429 INL_Component_State.songs_list->Render() | frame | flex |
1432 borderHeavy | color(GetCurrWinColor(!focus_on_artists));
1434 auto panes = vbox({hbox({
1435 left_pane | size(WIDTH, EQUAL, 100) | size(HEIGHT, EQUAL, 100) | flex,
1436 right_pane | size(WIDTH, EQUAL, 100) | size(HEIGHT, EQUAL, 100) | flex,
1441 auto progress_style = INL_Thread_State.is_playing
1442 ? color(global_colors.progress_bar_playing_col)
1443 : color(global_colors.progress_bar_not_playing_col);
1444 auto progress_bar = hbox({
1445 text(
FormatTime((
int)current_position)) | progress_style,
1446 gauge(progress) | flex | progress_style,
1447 text(
FormatTime(GetCurrentSongDuration())) | progress_style,
1450 auto volume_bar = hbox({
1451 text(
" Vol: ") | dim,
1452 gauge(volume / 100.0) | size(WIDTH, EQUAL, 10) | color(global_colors.volume_bar_col),
1453 text(std::to_string(volume) +
"%") | dim,
1456 std::string queue_info =
" ";
1457 int songs_left = song_queue.size() - current_song_queue_index - 1;
1458 if (songs_left > song_queue.size())
1460 queue_info += std::to_string(songs_left) +
" songs left.";
1461 std::string up_next_song =
" Next up: ";
1462 if (song_queue.size() > 1 && song_queue[current_song_queue_index + 1].metadata.title !=
"")
1463 up_next_song += song_queue[current_song_queue_index + 1].metadata.title +
" by " +
1464 song_queue[current_song_queue_index + 1].metadata.artist;
1466 up_next_song +=
"Next song not available.";
1467 auto queue_bar = hbox({
1468 text(queue_info) | dim | border | bold,
1474 text(current_playing_state.artist) | color(global_colors.status_bar_artist_col) | bold |
1476 text(current_song_info) | bold | color(global_colors.status_bar_song_col) |
1481 size(WIDTH, LESS_THAN, 15),
1484 size(HEIGHT, EQUAL, 1) | bgcolor(global_colors.status_bar_bg);
1489 progress_bar | border | flex,
1490 volume_bar | border,
1503 std::unique_lock<std::mutex> lock(INL_Thread_State.play_mutex);
1506 audio_player->stop();
1510 if (INL_Thread_State.play_future.valid())
1512 auto status = INL_Thread_State.play_future.wait_for(std::chrono::seconds(1));
1513 if (status != std::future_status::ready)
1516 dialog_message =
"Warning: Audio shutdown timed out";
1523 screen_->ExitLoopClosure()();