modified: src/main_screen.rs

modified:   src/playlist_icon.rs
This commit is contained in:
Michael Wain 2025-02-11 17:32:15 +03:00
parent ded8897ece
commit 8c3d87133d
2 changed files with 51 additions and 36 deletions

View File

@ -261,7 +261,7 @@ impl MainScreen {
for (i, row) in rows.iter().enumerate() { for (i, row) in rows.iter().enumerate() {
let cols = Layout::default() let cols = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints(vec![Constraint::Length(16); 2]) // Three columns .constraints(vec![Constraint::Percentage(33); 3]) // Three columns
.split(*row); .split(*row);
for (j, col) in cols.iter().enumerate() { for (j, col) in cols.iter().enumerate() {
@ -270,13 +270,19 @@ impl MainScreen {
let p = &v[index]; let p = &v[index];
/*let url_cl = p.thumbnail_url.clone(); /*let url_cl = p.thumbnail_url.clone();
let s = url_cl.lines().map(Line::from).collect::<Vec<Line>>(); let s = url_cl.lines().map(Line::from).collect::<Vec<Line>>();*/
let paragraph = Paragraph::new(s) let paragraph = Paragraph::new(Line::from(p.name.clone()))
.block(Block::default().borders(Borders::ALL)) .block(Block::default().borders(Borders::ALL))
.style(Style::default());*/ .style(Style::default());
frame.render_widget(p.thumbnail.clone(), *col); let pl = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)])
.split(*col);
frame.render_widget(p.thumbnail.clone(), pl[0]);
frame.render_widget(paragraph, pl[1]);
} }
} }
} }

View File

@ -1,34 +1,30 @@
use std::collections::HashSet; use image::DynamicImage;
use ratatui::{buffer::Buffer, layout::Rect, style::Color, widgets::Widget};
use color_eyre::owo_colors::OwoColorize;
use image::{DynamicImage, GenericImageView};
use ratatui::{
buffer::Buffer,
layout::Rect,
style::{Color, Style, Stylize},
widgets::Widget,
};
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct PlaylistIcon { pub struct PlaylistIcon {
colors: [[u8; 3]; 8], colors: Vec<[u8; 3]>,
} }
impl PlaylistIcon { impl PlaylistIcon {
pub fn new(img: DynamicImage) -> Self { pub fn new(img: DynamicImage) -> Self {
let pixels = img let img_rgb = img.to_rgb8();
.resize_exact(8, 8, image::imageops::FilterType::Nearest) let pixels = img_rgb.as_raw();
.to_rgb8()
.pixels() let r = color_thief::get_palette(pixels, color_thief::ColorFormat::Rgb, 10, 4)
.map(|p| p.0) .unwrap()
.collect::<HashSet<[u8; 3]>>()
.iter() .iter()
.copied() .map(|c| [c.r, c.g, c.b])
.collect::<Vec<[u8; 3]>>(); .collect::<Vec<[u8; 3]>>();
Self { Self { colors: r }
colors: pixels[..8].try_into().unwrap(), }
}
fn lerp(a: &[u8; 3], b: &[u8; 3], n: f32) -> [u8; 3] {
let r = (b[0] as f32 - a[0] as f32) * n + b[0] as f32;
let g = (b[1] as f32 - a[1] as f32) * n + b[1] as f32;
let b = (b[2] as f32 - a[2] as f32) * n + b[2] as f32;
[r as u8, g as u8, b as u8]
} }
} }
@ -36,17 +32,30 @@ impl Widget for PlaylistIcon {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let mut i = 0; let mut i = 0;
for x in area.left()..area.right() { let start = self.colors.first().unwrap_or(&[255u8, 255u8, 255u8]);
for y in area.top()..area.bottom() { let end = &[0u8, 0u8, 0u8];
let color = self.colors[i];
buf.set_string( let mut c = *start;
x,
y, for y in area.top()..area.bottom() {
"", for x in area.left()..area.right() {
Style::default().fg(Color::Rgb(color[0], color[1], color[2])), let n = (((area.bottom() - y).pow(2) + (area.right() - x).pow(2)) as f32).sqrt();
); c = PlaylistIcon::lerp(start, end, n);
i = if i >= self.colors.len() - 1 { 0 } else { i + 1 }; buf[(x, y)]
.set_char(' ')
.set_bg(Color::Rgb(c[0], c[1], c[2]))
.set_fg(Color::Rgb(c[0], c[1], c[2]));
} }
i = match self.colors.len() {
0 => 0,
_ => {
if i >= self.colors.len() - 1 {
0
} else {
i + 1
}
}
};
} }
} }
} }