use ratatui::{
layout::Rect,
style::{Style, Stylize},
text::{Line, Span},
widgets::{Block, BorderType, Borders, List, ListItem, ListState},
};
use crate::app::Config;
use crate::app::SongInfo;
use crate::app::ui::{RENDER_CACHE, WIDTH_CACHE, rendering::utils};
pub fn create_queue_widget<'a>(
queue: &[SongInfo],
queue_list_state: &ListState,
current_song: &Option<SongInfo>,
config: &Config,
area: Rect,
) -> List<'a> {
let border_color = config.colors.border_color();
let border_title_color = config.colors.border_title_color();
let queue_album_color = config.colors.queue_album_color();
let queue_artist_color = config.colors.queue_artist_color();
let queue_song_title_color = config.colors.queue_song_title_color();
let queue_position_color = config.colors.queue_position_color();
let queue_duration_color = config.colors.queue_duration_color();
let inner_width = area.width.saturating_sub(4) as usize;
let queue_items: Vec<ListItem> = if queue.is_empty() {
vec![]
} else {
let max_num_width = RENDER_CACHE.with(|cache| {
let cache = cache.borrow();
queue
.iter()
.enumerate()
.take(1000) .map(|(i, _)| unicode_width::UnicodeWidthStr::width(cache.queue_positions.get(i)))
.max()
.unwrap_or(3) });
queue
.iter()
.enumerate()
.map(|(i, song)| {
let separator_width = 3; let duration_display_width = 4; let remaining_width = inner_width
.saturating_sub(max_num_width + separator_width * 2 + duration_display_width);
let field_width = remaining_width / 3;
let duration_str = RENDER_CACHE.with(|cache| match song.duration {
Some(duration) => {
let mut cache = cache.borrow_mut();
cache.durations.format_short(duration.as_secs()).to_owned()
}
None => " (--:--)".to_owned(),
});
let field_width_max = field_width.max(8);
let (title, artist, album) = WIDTH_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
(
utils::left_align_cached(&mut cache, &song.title, field_width_max),
utils::left_align_cached(&mut cache, &song.artist, field_width_max),
utils::left_align_cached(&mut cache, &song.album, field_width_max),
)
});
let is_currently_playing = current_song
.as_ref()
.map(|current| current.file_path == song.file_path)
.unwrap_or(false);
let is_selected = queue_list_state.selected() == Some(i);
let mut queue_album_color = Style::default().fg(queue_album_color);
let mut queue_song_title_color = Style::default().fg(queue_song_title_color);
let mut queue_artist_color = Style::default().fg(queue_artist_color);
let mut border_color = Style::default().fg(border_color);
let mut duration_color = Style::default().fg(queue_duration_color);
let mut pos_color = Style::default().fg(queue_position_color);
if is_selected {
queue_album_color = queue_album_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
queue_song_title_color = queue_song_title_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
queue_artist_color = queue_artist_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
border_color = border_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
duration_color = duration_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
pos_color = pos_color
.bg(config.colors.queue_selected_highlight_color())
.fg(config.colors.queue_selected_text_color());
}
if is_currently_playing {
queue_album_color = queue_album_color.bold().italic();
queue_song_title_color = queue_song_title_color.bold().italic();
queue_artist_color = queue_artist_color.bold().italic();
border_color = border_color.bold().italic();
duration_color = duration_color.bold().italic();
pos_color = pos_color.bold().italic();
}
let num_str = RENDER_CACHE.with(|cache| {
let cache = cache.borrow();
cache.queue_positions.get(i).to_owned()
});
let padded_num_str = format!("{:<width$}", num_str, width = max_num_width);
let mut spans = vec![Span::styled(padded_num_str, pos_color)];
spans.push(Span::styled(title.clone(), queue_song_title_color));
spans.push(Span::styled(" ║ ", border_color));
spans.push(Span::styled(artist.clone(), queue_artist_color));
spans.push(Span::styled(" ║ ", border_color));
spans.push(Span::styled(album.clone(), queue_album_color));
spans.push(Span::styled(duration_str.clone(), duration_color));
if is_selected {
let line_content = format!(
"{} ║ {} ║ {}{}",
title.clone(),
artist.clone(),
album.clone(),
duration_str.clone()
);
let current_width =
unicode_width::UnicodeWidthStr::width(&line_content as &str)
+ max_num_width;
let remaining_width = area.width.saturating_sub(current_width as u16) as usize;
if remaining_width > 0 {
let padding = RENDER_CACHE.with(|cache| {
cache.borrow().fillers.spaces(remaining_width).to_owned()
});
spans.push(Span::styled(padding, border_color));
}
}
ListItem::new(Line::from(spans))
})
.collect::<Vec<_>>()
};
List::new(queue_items)
.block(
Block::default()
.border_type(BorderType::Rounded)
.borders(Borders::ALL)
.title(Span::styled(
" Queue ",
Style::default().fg(border_title_color),
))
.border_style(Style::default().fg(border_color)),
)
.style(Style::default().fg(border_color))
.highlight_style(Style::default())
.repeat_highlight_symbol(true)
}