обновление
This commit is contained in:
119
src/ui.rs
119
src/ui.rs
@@ -1,6 +1,6 @@
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::widgets::{
|
||||
Bar, BarChart, BarGroup, Block, Borders, List, ListItem, Paragraph,
|
||||
Bar, BarChart, BarGroup, Block, Borders, Clear, List, ListItem, Paragraph,
|
||||
};
|
||||
|
||||
use crate::app::{App, InputMode, Panel};
|
||||
@@ -26,9 +26,15 @@ pub fn draw(f: &mut Frame, app: &mut App) {
|
||||
draw_right_panel(f, app, top[1]);
|
||||
draw_equalizer(f, app, chunks[1]);
|
||||
draw_status(f, app, chunks[2]);
|
||||
|
||||
if app.show_help {
|
||||
draw_help(f, f.area());
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_left_panel(f: &mut Frame, app: &mut App, area: Rect) {
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
let items: Vec<ListItem> = app
|
||||
.library
|
||||
.artists
|
||||
@@ -49,8 +55,14 @@ fn draw_left_panel(f: &mut Frame, app: &mut App, area: Rect) {
|
||||
};
|
||||
app.library.left_state.select(sel);
|
||||
|
||||
let title = match app.library.sort_mode {
|
||||
SortMode::ByArtist => " Исполнители ",
|
||||
SortMode::ByAlbum => " Альбомы ",
|
||||
SortMode::ByFilename => " Источник ",
|
||||
};
|
||||
|
||||
let list = List::new(items)
|
||||
.block(Block::default().borders(Borders::ALL).title(" Исполнители "))
|
||||
.block(Block::default().borders(Borders::ALL).title(title))
|
||||
.highlight_style(highlight)
|
||||
.highlight_symbol("> ");
|
||||
|
||||
@@ -66,6 +78,8 @@ fn fmt_dur(d: std::time::Duration) -> String {
|
||||
}
|
||||
|
||||
fn draw_right_panel(f: &mut Frame, app: &mut App, area: Rect) {
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
let rows = app.library.current_right_rows();
|
||||
|
||||
let items: Vec<ListItem> = rows
|
||||
@@ -137,20 +151,31 @@ fn draw_right_panel(f: &mut Frame, app: &mut App, area: Rect) {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let selected_name = app
|
||||
.library
|
||||
.artists
|
||||
.get(app.library.artist_selected)
|
||||
.map(|a| a.name.as_str())
|
||||
.unwrap_or("—");
|
||||
|
||||
let title = match app.library.sort_mode {
|
||||
SortMode::ByArtist => {
|
||||
let artist_name = app
|
||||
.library
|
||||
.artists
|
||||
.get(app.library.artist_selected)
|
||||
.map(|a| a.name.as_str())
|
||||
.unwrap_or("—");
|
||||
if app.library.filter.is_empty() {
|
||||
format!(" Альбомы и треки — {} ", artist_name)
|
||||
format!(" Альбомы и треки — {} ", selected_name)
|
||||
} else {
|
||||
format!(
|
||||
" Альбомы и треки — {} [{}] ",
|
||||
artist_name, app.library.filter
|
||||
selected_name, app.library.filter
|
||||
)
|
||||
}
|
||||
}
|
||||
SortMode::ByAlbum => {
|
||||
if app.library.filter.is_empty() {
|
||||
format!(" Исполнители и треки — {} ", selected_name)
|
||||
} else {
|
||||
format!(
|
||||
" Исполнители и треки — {} [{}] ",
|
||||
selected_name, app.library.filter
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -235,8 +260,6 @@ fn draw_equalizer(f: &mut Frame, app: &App, area: Rect) {
|
||||
}
|
||||
|
||||
fn draw_status(f: &mut Frame, app: &App, area: Rect) {
|
||||
// Раскладка нижней строки:
|
||||
// [ Статус ................. ] [ 01:23 / 03:45 ] [ Mode ] [ vol: NN% ]
|
||||
let inner = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([
|
||||
@@ -247,7 +270,6 @@ fn draw_status(f: &mut Frame, app: &App, area: Rect) {
|
||||
])
|
||||
.split(area);
|
||||
|
||||
// ── Статус / поиск / пауза ────────────────────────────────
|
||||
let (status_text, status_title) = if app.input_mode == InputMode::Search {
|
||||
(format!("> {}", app.search_buffer), " Поиск ")
|
||||
} else {
|
||||
@@ -259,7 +281,6 @@ fn draw_status(f: &mut Frame, app: &App, area: Rect) {
|
||||
.block(Block::default().borders(Borders::ALL).title(status_title));
|
||||
f.render_widget(status, inner[0]);
|
||||
|
||||
// ── Время: текущее / полное ───────────────────────────────
|
||||
let (pos, total) = app.progress();
|
||||
let time_text = format!(
|
||||
"{:02}:{:02} / {:02}:{:02}",
|
||||
@@ -270,11 +291,9 @@ fn draw_status(f: &mut Frame, app: &App, area: Rect) {
|
||||
);
|
||||
let time = Paragraph::new(time_text)
|
||||
.alignment(Alignment::Center)
|
||||
.style(Style::default().fg(Color::Rgb(70, 130, 180)))
|
||||
.block(Block::default().borders(Borders::ALL).title(" Время "));
|
||||
f.render_widget(time, inner[1]);
|
||||
|
||||
// ── Repeat / Shuffle ──────────────────────────────────────
|
||||
let mode_str = match (app.repeat, app.shuffle) {
|
||||
(RepeatMode::Off, false) => "".to_string(),
|
||||
(RepeatMode::Off, true) => "SHUF".to_string(),
|
||||
@@ -288,10 +307,74 @@ fn draw_status(f: &mut Frame, app: &App, area: Rect) {
|
||||
.block(Block::default().borders(Borders::ALL).title(" Mode "));
|
||||
f.render_widget(mode, inner[2]);
|
||||
|
||||
// ── Громкость ─────────────────────────────────────────────
|
||||
let vol_text = format!("vol: {}%", (app.player.volume() * 100.0) as u32);
|
||||
let vol = Paragraph::new(vol_text)
|
||||
.alignment(Alignment::Center)
|
||||
.block(Block::default().borders(Borders::ALL).title(" Звук "));
|
||||
.block(Block::default().borders(Borders::ALL).title(" "));
|
||||
f.render_widget(vol, inner[3]);
|
||||
}
|
||||
|
||||
fn draw_help(f: &mut Frame, screen: Rect) {
|
||||
let width = 60u16.min(screen.width.saturating_sub(4));
|
||||
let height = 34u16.min(screen.height.saturating_sub(4));
|
||||
|
||||
let x = (screen.width.saturating_sub(width)) / 2;
|
||||
let y = (screen.height.saturating_sub(height)) / 2;
|
||||
let area = Rect::new(x, y, width, height);
|
||||
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
let header = Style::default()
|
||||
.fg(Color::Yellow)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let title_color = Style::default().fg(Color::Rgb(100, 149, 237));
|
||||
|
||||
let help_text = vec![
|
||||
Line::from(Span::styled(
|
||||
" Управление плеером ",
|
||||
title_color.add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(""),
|
||||
Line::from(Span::styled(" Навигация", header)),
|
||||
Line::from(" w / ↑ вверх по списку"),
|
||||
Line::from(" s / ↓ вниз по списку"),
|
||||
Line::from(" Tab переключить панель"),
|
||||
Line::from(" g / G в начало / в конец списка"),
|
||||
Line::from(" f к играющему треку"),
|
||||
Line::from(""),
|
||||
Line::from(Span::styled(" Воспроизведение", header)),
|
||||
Line::from(" Enter играть выделенный трек"),
|
||||
Line::from(" Space пауза / продолжить"),
|
||||
Line::from(" z стоп"),
|
||||
Line::from(" Ctrl+→ / Ctrl+d следующий трек"),
|
||||
Line::from(" Ctrl+← / Ctrl+a предыдущий трек"),
|
||||
Line::from(" r repeat: off → all → one"),
|
||||
Line::from(" x shuffle вкл/выкл"),
|
||||
Line::from(""),
|
||||
Line::from(Span::styled(" Перемотка", header)),
|
||||
Line::from(" a / ← −5 секунд"),
|
||||
Line::from(" d / → +5 секунд"),
|
||||
Line::from(" 1..9 перейти к 10%..90% трека"),
|
||||
Line::from(""),
|
||||
Line::from(Span::styled(" Звук и прочее", header)),
|
||||
Line::from(" + / - громкость"),
|
||||
Line::from(" / поиск"),
|
||||
Line::from(" t сортировка: артист → альбом → файл"),
|
||||
Line::from(" F1 эта справка"),
|
||||
Line::from(" q / Esc выход"),
|
||||
Line::from(""),
|
||||
Line::from(Span::styled(
|
||||
" F1 или Esc — закрыть ",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
)),
|
||||
];
|
||||
|
||||
let help = Paragraph::new(help_text).block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(title_color)
|
||||
.title(" Помощь "),
|
||||
);
|
||||
|
||||
f.render_widget(help, area);
|
||||
}
|
||||
Reference in New Issue
Block a user