Added network catalog slices
This commit is contained in:
+39
-7
@@ -12,7 +12,7 @@ use crate::app::state::{
|
||||
fed_release_groups, release_groups,
|
||||
};
|
||||
use crate::art::cache_key;
|
||||
use crate::library::models::{ArtistCard, ReleaseCard, SearchResults};
|
||||
use crate::library::models::{ArtistCard, Availability, ReleaseCard, SearchResults};
|
||||
|
||||
const TILE_MARQUEE_STEP_MS: u128 = 250;
|
||||
const TILE_MARQUEE_PAUSE_STEPS: u128 = 4;
|
||||
@@ -83,13 +83,14 @@ fn draw_art(frame: &mut Frame, area: Rect, art_state: Option<&ArtState>) {
|
||||
/// Bordered tile with artwork, a title line and a dim meta line. The
|
||||
/// selected tile gets a thick accent border and an inverted (filled)
|
||||
/// caption so it stands out in a large grid; the artwork stays untouched.
|
||||
fn draw_tile(
|
||||
fn draw_tile_with_availability(
|
||||
frame: &mut Frame,
|
||||
tile: Rect,
|
||||
art_state: Option<&ArtState>,
|
||||
title: &str,
|
||||
meta: &str,
|
||||
selected: bool,
|
||||
availability: Option<Availability>,
|
||||
) {
|
||||
let block = if selected {
|
||||
Block::bordered()
|
||||
@@ -106,6 +107,9 @@ fn draw_tile(
|
||||
..inner
|
||||
};
|
||||
draw_art(frame, art_area, art_state);
|
||||
if let Some(availability) = availability {
|
||||
draw_availability_badge(frame, art_area, availability);
|
||||
}
|
||||
|
||||
if inner.height > ART_CELL_HEIGHT {
|
||||
let name_area = Rect {
|
||||
@@ -137,6 +141,27 @@ fn draw_tile(
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_availability_badge(frame: &mut Frame, area: Rect, availability: Availability) {
|
||||
if area.width < 2 || area.height == 0 {
|
||||
return;
|
||||
}
|
||||
let (label, style) = match availability {
|
||||
Availability::Local => ("●", Style::new().fg(Color::Green)),
|
||||
Availability::Mixed => ("◐", Style::new().fg(Color::Yellow)),
|
||||
Availability::Remote => ("⇅", theme::accent()),
|
||||
};
|
||||
let badge = Rect {
|
||||
x: area.x + area.width.saturating_sub(2),
|
||||
y: area.y,
|
||||
width: 2,
|
||||
height: 1,
|
||||
};
|
||||
frame.render_widget(
|
||||
Paragraph::new(Line::styled(label, style)).alignment(Alignment::Right),
|
||||
badge,
|
||||
);
|
||||
}
|
||||
|
||||
fn tile_title(title: &str, width: u16, selected: bool) -> String {
|
||||
let width = usize::from(width);
|
||||
if width == 0 {
|
||||
@@ -272,9 +297,13 @@ fn scroll_offset(items: &[PlanItem], cursor_item: Option<usize>, viewport: u16)
|
||||
fn draw_grid(frame: &mut Frame, area: Rect, state: &AppState) {
|
||||
let global = &state.global;
|
||||
let title = if global.total > 0 {
|
||||
format!(" Library — {} artists ", global.total)
|
||||
format!(
|
||||
" Library — {} artists · {} ",
|
||||
global.total,
|
||||
global.filters.source_mode.label()
|
||||
)
|
||||
} else {
|
||||
" Library ".to_string()
|
||||
format!(" Library · {} ", global.filters.source_mode.label())
|
||||
};
|
||||
let mut title_spans = vec![Span::styled(title, theme::tab_active())];
|
||||
if global.filters.is_active() {
|
||||
@@ -323,13 +352,14 @@ fn draw_grid_tiles(frame: &mut Frame, inner: Rect, state: &AppState) {
|
||||
width: TILE_WIDTH,
|
||||
height: TILE_HEIGHT,
|
||||
};
|
||||
draw_tile(
|
||||
draw_tile_with_availability(
|
||||
frame,
|
||||
tile,
|
||||
tile_art(state, artist.image_path.as_ref()),
|
||||
&artist.name,
|
||||
&artist_tile_meta(artist),
|
||||
index == global.selected,
|
||||
Some(artist.availability),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -557,13 +587,14 @@ fn draw_artist(frame: &mut Frame, area: Rect, state: &AppState, id: i64, cursor:
|
||||
if tile.width < 3 {
|
||||
break;
|
||||
}
|
||||
draw_tile(
|
||||
draw_tile_with_availability(
|
||||
frame,
|
||||
tile,
|
||||
tile_art(state, release.cover_path.as_ref()),
|
||||
&release.title,
|
||||
&release_tile_meta(release),
|
||||
cursor == tracks + position,
|
||||
Some(release.availability),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1055,13 +1086,14 @@ fn draw_fed_artist(frame: &mut Frame, area: Rect, state: &AppState, cursor: usiz
|
||||
if let Some(year) = release.year {
|
||||
meta = format!("{meta} · {year}");
|
||||
}
|
||||
draw_tile(
|
||||
draw_tile_with_availability(
|
||||
frame,
|
||||
tile,
|
||||
tile_art(state, release.cover_path.as_ref()),
|
||||
&release.title,
|
||||
&meta,
|
||||
cursor == *position,
|
||||
Some(Availability::Remote),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-16
@@ -371,7 +371,7 @@ fn clip_cells(text: &str, max_width: usize) -> String {
|
||||
}
|
||||
|
||||
fn draw_library_filters(frame: &mut Frame, state: &AppState, cursor: usize) {
|
||||
let area = centered(frame.area(), 46, 6);
|
||||
let area = centered(frame.area(), 54, 9);
|
||||
let block = Block::bordered()
|
||||
.title(" Library filters ")
|
||||
.title_style(theme::header())
|
||||
@@ -381,33 +381,47 @@ fn draw_library_filters(frame: &mut Frame, state: &AppState, cursor: usize) {
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let [list_area, _, footer] = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(4),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.areas(inner);
|
||||
|
||||
let mut rows: Vec<Line<'static>> = Vec::new();
|
||||
let checked = if state.global.filters.hide_featured_only {
|
||||
"[x]"
|
||||
} else {
|
||||
"[ ]"
|
||||
};
|
||||
let row = Rect {
|
||||
height: 1,
|
||||
..list_area
|
||||
};
|
||||
frame.render_widget(
|
||||
Paragraph::new(Line::from(vec![
|
||||
Span::styled(format!("{checked} "), theme::accent()),
|
||||
Span::raw("Hide featured only"),
|
||||
])),
|
||||
row,
|
||||
);
|
||||
if cursor == 0 {
|
||||
frame.buffer_mut().set_style(row, theme::tab_active());
|
||||
rows.push(Line::from(vec![
|
||||
Span::styled(format!("{checked} "), theme::accent()),
|
||||
Span::raw("Hide featured only"),
|
||||
]));
|
||||
for mode in crate::config::settings::LibrarySourceMode::ALL {
|
||||
let marker = if state.global.filters.source_mode == mode {
|
||||
"(*)"
|
||||
} else {
|
||||
"( )"
|
||||
};
|
||||
rows.push(Line::from(vec![
|
||||
Span::styled(format!("{marker} "), theme::accent()),
|
||||
Span::raw(mode.label()),
|
||||
Span::styled(format!(" {}", mode.description()), theme::dim()),
|
||||
]));
|
||||
}
|
||||
for (index, line) in rows.into_iter().enumerate() {
|
||||
let row = Rect {
|
||||
y: list_area.y + index as u16,
|
||||
height: 1,
|
||||
..list_area
|
||||
};
|
||||
frame.render_widget(Paragraph::new(line), row);
|
||||
if cursor == index {
|
||||
frame.buffer_mut().set_style(row, theme::tab_active());
|
||||
}
|
||||
}
|
||||
frame.render_widget(
|
||||
Paragraph::new(Line::styled("space/enter toggle · esc close", theme::dim()))
|
||||
Paragraph::new(Line::styled("space/enter select · esc close", theme::dim()))
|
||||
.alignment(Alignment::Center),
|
||||
footer,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user