modified: src/config.rs
modified: src/main.rs new file: src/main_screen.rs modified: src/screen.rs new file: src/sync.rs deleted: src/tabs.rs new file: src/wait_screen.rs
This commit is contained in:
parent
60e92ee1d7
commit
0e7d9aa8d9
@ -1,5 +1,13 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
pub fn get_configs_dir() -> PathBuf {
|
||||
let mut p = dirs::home_dir().unwrap();
|
||||
p.push(".lyrica");
|
||||
p
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct YouTubeConfiguration {
|
||||
pub user_id: u64
|
||||
|
181
src/main.rs
181
src/main.rs
@ -1,103 +1,32 @@
|
||||
use std::{error::Error, io, path::{Path, PathBuf}};
|
||||
use std::{any::Any, cell::RefCell, collections::HashMap, error::Error, io, ops::Deref, path::{Path, PathBuf}};
|
||||
|
||||
use color_eyre::Result;
|
||||
use config::LyricaConfiguration;
|
||||
use crossterm::{event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyEventKind}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}};
|
||||
use ratatui::{buffer::Buffer, layout::{Layout, Rect}, prelude::{Backend, CrosstermBackend}, style::{Color, Stylize}, symbols::border, text::{Line, Text}, widgets::{Block, Paragraph, Tabs, Widget}, DefaultTerminal, Frame, Terminal};
|
||||
use screen::MainScreen;
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
use strum::IntoEnumIterator;
|
||||
use main_screen::MainScreen;
|
||||
use screen::AppScreen;
|
||||
use sync::AppEvent;
|
||||
use tokio::{fs::File, io::AsyncReadExt, sync::mpsc::{self, Receiver, Sender, UnboundedReceiver, UnboundedSender}};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use itunesdb::xobjects::XDatabase;
|
||||
use ratatui::prelude::Constraint::{Length, Min};
|
||||
use wait_screen::WaitScreen;
|
||||
|
||||
mod util;
|
||||
mod config;
|
||||
mod tabs;
|
||||
mod screen;
|
||||
mod main_screen;
|
||||
mod wait_screen;
|
||||
mod sync;
|
||||
|
||||
fn get_configs_dir() -> PathBuf {
|
||||
let mut p = dirs::home_dir().unwrap();
|
||||
p.push(".lyrica");
|
||||
p
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Eq, Hash, PartialEq)]
|
||||
enum AppState {
|
||||
IPodWait,
|
||||
MainScreen(crate::screen::MainScreen)
|
||||
MainScreen
|
||||
}
|
||||
|
||||
enum AppEvent {
|
||||
SearchIPod,
|
||||
IPodFound(String),
|
||||
IPodNotFound,
|
||||
ParseItunes(String),
|
||||
ITunesParsed(XDatabase),
|
||||
SoundcloudGot(CloudPlaylists)
|
||||
}
|
||||
|
||||
fn initialize_async_service(sender: Sender<AppEvent>, receiver: UnboundedReceiver<AppEvent>, token: CancellationToken) {
|
||||
tokio::spawn(async move {
|
||||
let mut receiver = receiver;
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = token.cancelled() => { return; }
|
||||
r = receiver.recv() => {
|
||||
if let Some(request) = r {
|
||||
match request {
|
||||
AppEvent::SearchIPod => {
|
||||
/*if let Some(p) = util::search_ipod() {
|
||||
let _ = sender.send(AppEvent::IPodFound(p)).await;
|
||||
} else {
|
||||
let _ = sender.send(AppEvent::IPodNotFound).await;
|
||||
}*/
|
||||
let _ = sender.send(AppEvent::IPodFound("D:\\Documents\\RustroverProjects\\itunesdb\\ITunesDB\\two_tracks".to_string())).await;
|
||||
},
|
||||
AppEvent::ParseItunes(path) => {
|
||||
// todo: parse itunes
|
||||
let _ = std::fs::create_dir_all(get_configs_dir());
|
||||
let mut cd = get_configs_dir();
|
||||
cd.push("idb");
|
||||
let mut p: PathBuf = Path::new(&path).into();
|
||||
// p.push("iPod_Control");
|
||||
// p.push("iTunes");
|
||||
// p.set_file_name("iTunesDB");
|
||||
let _ = std::fs::copy(p, &cd);
|
||||
let mut file = File::open(cd).await.unwrap();
|
||||
let mut contents = vec![];
|
||||
file.read_to_end(&mut contents).await.unwrap();
|
||||
let xdb = itunesdb::deserializer::parse_bytes(&contents);
|
||||
let _ = sender.send(AppEvent::ITunesParsed(xdb)).await;
|
||||
|
||||
let mut p = get_configs_dir();
|
||||
p.push("config");
|
||||
p.set_extension(".toml");
|
||||
if !p.exists() { return; }
|
||||
let mut file = File::open(p).await.unwrap();
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content).await.unwrap();
|
||||
let config: LyricaConfiguration = toml::from_str(&content).unwrap();
|
||||
|
||||
let app_version = soundcloud::get_app().await.unwrap().unwrap();
|
||||
let client_id = soundcloud::get_client_id().await.unwrap().unwrap();
|
||||
let playlists = soundcloud::get_playlists(config.get_soundcloud().user_id, client_id, app_version).await.unwrap();
|
||||
|
||||
let _ = sender.send(AppEvent::SoundcloudGot(playlists)).await;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
state: AppState,
|
||||
screens: HashMap<AppState, Box<dyn AppScreen>>,
|
||||
receiver: Receiver<AppEvent>,
|
||||
sender: UnboundedSender<AppEvent>,
|
||||
token: CancellationToken,
|
||||
@ -108,9 +37,16 @@ impl Default for App {
|
||||
let (tx, mut rx) = mpsc::channel(1);
|
||||
let (jx, mut jr) = mpsc::unbounded_channel();
|
||||
let token = CancellationToken::new();
|
||||
initialize_async_service(tx, jr, token.clone());
|
||||
|
||||
sync::initialize_async_service(tx, jr, token.clone());
|
||||
|
||||
let _ = jx.send(AppEvent::SearchIPod);
|
||||
Self { state: AppState::IPodWait, receiver: rx, sender: jx, token }
|
||||
|
||||
let mut screens: HashMap<AppState, Box<dyn AppScreen>> = HashMap::new();
|
||||
screens.insert(AppState::IPodWait, Box::new(WaitScreen::default()));
|
||||
screens.insert(AppState::MainScreen, Box::new(MainScreen::new()));
|
||||
|
||||
Self { receiver: rx, sender: jx, token, state: AppState::IPodWait, screens }
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,8 +59,8 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(&self, frame: &mut Frame) {
|
||||
frame.render_widget(self.state.clone(), frame.area());
|
||||
fn draw(&mut self, frame: &mut Frame) {
|
||||
self.screens.get(&self.state).unwrap().render(frame);
|
||||
}
|
||||
|
||||
fn handle_events(&mut self) -> io::Result<()> {
|
||||
@ -137,7 +73,7 @@ impl App {
|
||||
if let Ok(event) = self.receiver.try_recv() {
|
||||
match event {
|
||||
AppEvent::IPodFound(path) => {
|
||||
self.state = AppState::MainScreen(MainScreen::new());
|
||||
self.state = AppState::MainScreen;
|
||||
let _ = self.sender.send(AppEvent::ParseItunes(path));
|
||||
},
|
||||
AppEvent::IPodNotFound => {
|
||||
@ -147,11 +83,12 @@ impl App {
|
||||
|
||||
},
|
||||
AppEvent::SoundcloudGot(playlists) => {
|
||||
if let AppState::MainScreen(screen) = &self.state {
|
||||
let mut screen = screen.clone();
|
||||
screen.soundcloud = Some(playlists);
|
||||
self.state = AppState::MainScreen(screen);
|
||||
}
|
||||
let a = self.screens.get_mut(&AppState::MainScreen).unwrap();
|
||||
let screen: &mut MainScreen = match a.as_any().downcast_mut::<MainScreen>() {
|
||||
Some(b) => b,
|
||||
None => panic!("&a isn't a B!"),
|
||||
};
|
||||
screen.soundcloud = Some(playlists);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -160,11 +97,7 @@ impl App {
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||
if let AppState::MainScreen(screen) = &self.state {
|
||||
let mut screen = screen.clone();
|
||||
screen.handle_key_event(key_event);
|
||||
self.state = AppState::MainScreen(screen);
|
||||
}
|
||||
self.screens.get_mut(&self.state).unwrap().handle_key_event(key_event);
|
||||
match key_event.code {
|
||||
KeyCode::Char('q') => self.exit(),
|
||||
_ => {}
|
||||
@ -176,62 +109,10 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
fn render_main_screen(area: Rect, buf: &mut Buffer, screen: &mut MainScreen) {
|
||||
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
|
||||
let [header_area, inner_area, footer_area] = vertical.areas(area);
|
||||
|
||||
let horizontal = Layout::horizontal([Min(0), Length(7)]);
|
||||
let [tabs_area, title_area] = horizontal.areas(header_area);
|
||||
|
||||
MainScreen::render_title(title_area, buf);
|
||||
screen.render_tabs(tabs_area, buf);
|
||||
screen.selected_tab.render(inner_area, buf);
|
||||
MainScreen::render_footer(footer_area, buf);
|
||||
}
|
||||
|
||||
fn render_waiting_screen(area: Rect, buf: &mut Buffer) {
|
||||
let title = Line::from(" Lyrica ".bold());
|
||||
let instructions = Line::from(vec![
|
||||
" Quit ".into(),
|
||||
"<Q> ".red().bold(),
|
||||
]);
|
||||
let block = Block::bordered()
|
||||
.title(title.centered())
|
||||
.title_bottom(instructions.centered())
|
||||
.border_set(border::ROUNDED);
|
||||
|
||||
let counter_text = Text::from(
|
||||
vec![
|
||||
Line::from(
|
||||
vec![
|
||||
"Searching for iPod...".into()
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
Paragraph::new(counter_text)
|
||||
.centered()
|
||||
.block(block)
|
||||
.render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for AppState {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
match self {
|
||||
AppState::IPodWait => AppState::render_waiting_screen(area, buf),
|
||||
AppState::MainScreen(mut s) => AppState::render_main_screen(area, buf, &mut s),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
enable_raw_mode()?;
|
||||
let mut stderr = io::stderr(); // This is a special case. Normally using stdout is fine
|
||||
let mut stderr = io::stdout();
|
||||
execute!(stderr, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
let backend = CrosstermBackend::new(stderr);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
73
src/main_screen.rs
Normal file
73
src/main_screen.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use ratatui::{buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, style::{Color, Modifier, Style, Stylize}, text::{Line, Span}, widgets::{Block, Borders, Paragraph, Tabs, Widget}, Frame};
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::screen::AppScreen;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MainScreen {
|
||||
selected_tab: u8,
|
||||
tab_titles: Vec<String>,
|
||||
pub soundcloud: Option<CloudPlaylists>
|
||||
}
|
||||
|
||||
impl AppScreen for MainScreen {
|
||||
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||
/*match key_event.code {
|
||||
KeyCode::Char('l') | KeyCode::Right => self.next_tab(),
|
||||
KeyCode::Char('h') | KeyCode::Left => self.previous_tab(),
|
||||
_ => {}
|
||||
}*/
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3), // Tabs
|
||||
Constraint::Min(0), // Main content area
|
||||
Constraint::Length(1), // Status bar
|
||||
])
|
||||
.split(frame.area());
|
||||
|
||||
let tabs = Tabs::new(
|
||||
self.tab_titles.iter().map(|t| Span::raw(t.clone())).collect::<Vec<Span>>(),
|
||||
)
|
||||
.block(Block::default().borders(Borders::ALL))
|
||||
.highlight_style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD))
|
||||
.select(self.selected_tab as usize)
|
||||
.style(Style::default().fg(Color::White));
|
||||
|
||||
frame.render_widget(tabs, chunks[0]);
|
||||
|
||||
let main_content = Paragraph::new("Main content goes here!")
|
||||
.block(Block::default().borders(Borders::ALL).title("Main"));
|
||||
frame.render_widget(main_content, chunks[1]); // Render into second chunk
|
||||
|
||||
// Render Status Bar
|
||||
let status_bar = Paragraph::new("Press 'q' to quit | Arrow keys to navigate")
|
||||
.style(Style::default().fg(Color::Cyan));
|
||||
frame.render_widget(status_bar, chunks[2]); // Render into third chunk
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl MainScreen {
|
||||
pub fn new() -> Self {
|
||||
MainScreen { soundcloud: None, selected_tab: 0, tab_titles: vec!["YouTube".to_string(), "SoundCloud".to_string(), "Local Playlists".to_string(), "Settings".to_string()] }
|
||||
}
|
||||
|
||||
pub fn render_title(area: Rect, buf: &mut Buffer) {
|
||||
"Lyrica".bold().render(area, buf);
|
||||
}
|
||||
|
||||
pub fn render_footer(area: Rect, buf: &mut Buffer) {
|
||||
Line::raw("◄ ► to change tab | <Q> to quit")
|
||||
.centered()
|
||||
.render(area, buf);
|
||||
}
|
||||
}
|
@ -1,56 +1,12 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Stylize}, text::Line, widgets::{Tabs, Widget}};
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
use strum::IntoEnumIterator;
|
||||
use std::any::Any;
|
||||
|
||||
use crate::tabs::SelectedTab;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::{buffer::Buffer, layout::Rect, Frame};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MainScreen {
|
||||
pub selected_tab: SelectedTab,
|
||||
pub soundcloud: Option<CloudPlaylists>
|
||||
}
|
||||
pub trait AppScreen {
|
||||
fn handle_key_event(&mut self, key_event: KeyEvent);
|
||||
|
||||
impl MainScreen {
|
||||
pub fn new() -> Self {
|
||||
MainScreen { selected_tab: SelectedTab::Playlists, soundcloud: None }
|
||||
}
|
||||
fn render(&self, frame: &mut Frame);
|
||||
|
||||
pub fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||
match key_event.code {
|
||||
KeyCode::Char('l') | KeyCode::Right => self.next_tab(),
|
||||
KeyCode::Char('h') | KeyCode::Left => self.previous_tab(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_title(area: Rect, buf: &mut Buffer) {
|
||||
"Lyrica".bold().render(area, buf);
|
||||
}
|
||||
|
||||
pub fn render_footer(area: Rect, buf: &mut Buffer) {
|
||||
Line::raw("◄ ► to change tab | <Q> to quit")
|
||||
.centered()
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
pub fn render_tabs(&self, area: Rect, buf: &mut Buffer) {
|
||||
let titles = SelectedTab::iter().map(SelectedTab::title);
|
||||
let highlight_style = (Color::default(), self.selected_tab.palette().c700);
|
||||
let selected_tab_index = self.selected_tab.to_usize();
|
||||
Tabs::new(titles)
|
||||
.highlight_style(highlight_style)
|
||||
.select(selected_tab_index)
|
||||
.padding("", "")
|
||||
.divider(" ")
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn next_tab(&mut self) {
|
||||
self.selected_tab = self.selected_tab.next();
|
||||
}
|
||||
|
||||
fn previous_tab(&mut self) {
|
||||
self.selected_tab = self.selected_tab.previous();
|
||||
}
|
||||
fn as_any(&mut self) -> &mut dyn Any;
|
||||
}
|
74
src/sync.rs
Normal file
74
src/sync.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use itunesdb::xobjects::XDatabase;
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
use tokio::{fs::File, io::AsyncReadExt, sync::mpsc::{Sender, UnboundedReceiver}};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::config::{get_configs_dir, LyricaConfiguration};
|
||||
|
||||
pub enum AppEvent {
|
||||
SearchIPod,
|
||||
IPodFound(String),
|
||||
IPodNotFound,
|
||||
ParseItunes(String),
|
||||
ITunesParsed(XDatabase),
|
||||
SoundcloudGot(CloudPlaylists)
|
||||
}
|
||||
|
||||
pub fn initialize_async_service(sender: Sender<AppEvent>, receiver: UnboundedReceiver<AppEvent>, token: CancellationToken) {
|
||||
tokio::spawn(async move {
|
||||
let mut receiver = receiver;
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = token.cancelled() => { return; }
|
||||
r = receiver.recv() => {
|
||||
if let Some(request) = r {
|
||||
match request {
|
||||
AppEvent::SearchIPod => {
|
||||
/*if let Some(p) = util::search_ipod() {
|
||||
let _ = sender.send(AppEvent::IPodFound(p)).await;
|
||||
} else {
|
||||
let _ = sender.send(AppEvent::IPodNotFound).await;
|
||||
}*/
|
||||
let _ = sender.send(AppEvent::IPodFound("D:\\Documents\\RustroverProjects\\itunesdb\\ITunesDB\\two_tracks".to_string())).await;
|
||||
},
|
||||
AppEvent::ParseItunes(path) => {
|
||||
// todo: parse itunes
|
||||
let _ = std::fs::create_dir_all(get_configs_dir());
|
||||
let mut cd = get_configs_dir();
|
||||
cd.push("idb");
|
||||
let mut p: PathBuf = Path::new(&path).into();
|
||||
// p.push("iPod_Control");
|
||||
// p.push("iTunes");
|
||||
// p.set_file_name("iTunesDB");
|
||||
let _ = std::fs::copy(p, &cd);
|
||||
let mut file = File::open(cd).await.unwrap();
|
||||
let mut contents = vec![];
|
||||
file.read_to_end(&mut contents).await.unwrap();
|
||||
let xdb = itunesdb::deserializer::parse_bytes(&contents);
|
||||
let _ = sender.send(AppEvent::ITunesParsed(xdb)).await;
|
||||
|
||||
let mut p = get_configs_dir();
|
||||
p.push("config");
|
||||
p.set_extension(".toml");
|
||||
if !p.exists() { return; }
|
||||
let mut file = File::open(p).await.unwrap();
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content).await.unwrap();
|
||||
let config: LyricaConfiguration = toml::from_str(&content).unwrap();
|
||||
|
||||
let app_version = soundcloud::get_app().await.unwrap().unwrap();
|
||||
let client_id = soundcloud::get_client_id().await.unwrap().unwrap();
|
||||
let playlists = soundcloud::get_playlists(config.get_soundcloud().user_id, client_id, app_version).await.unwrap();
|
||||
|
||||
let _ = sender.send(AppEvent::SoundcloudGot(playlists)).await;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
97
src/tabs.rs
97
src/tabs.rs
@ -1,97 +0,0 @@
|
||||
use ratatui::{buffer::Buffer, layout::Rect, style::{palette::tailwind, Stylize}, symbols, text::Line, widgets::{Block, Padding, Paragraph, Widget}};
|
||||
use soundcloud::sobjects::CloudPlaylists;
|
||||
use strum::{AsRefStr, Display, EnumIter, FromRepr, IntoEnumIterator};
|
||||
|
||||
use crate::screen::MainScreen;
|
||||
|
||||
#[derive(Debug, Default, Clone, Display, FromRepr, EnumIter, AsRefStr)]
|
||||
pub enum SelectedTab {
|
||||
#[default]
|
||||
#[strum(to_string = "Playlists")]
|
||||
Playlists,
|
||||
#[strum(to_string = "Albums")]
|
||||
Albums,
|
||||
#[strum(to_string = "Soundcloud")]
|
||||
Soundcloud(Option<CloudPlaylists>),
|
||||
#[strum(to_string = "Youtube")]
|
||||
Youtube,
|
||||
}
|
||||
|
||||
impl Widget for SelectedTab {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let block = self.block();
|
||||
match self {
|
||||
Self::Albums => self.render_albums(area, buf),
|
||||
Self::Playlists => self.render_playlists(area, buf),
|
||||
Self::Soundcloud(playlists) => SelectedTab::render_soundcloud(block,area, buf, playlists),
|
||||
Self::Youtube => self.render_youtube(area, buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectedTab {
|
||||
/// Return tab's name as a styled `Line`
|
||||
pub fn title(self) -> Line<'static> {
|
||||
format!(" {self} ")
|
||||
.fg(tailwind::SLATE.c200)
|
||||
.bg(self.palette().c900)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn render_albums(self, area: Rect, buf: &mut Buffer) {
|
||||
Paragraph::new("Hello, World!")
|
||||
.block(self.block())
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_playlists(self, area: Rect, buf: &mut Buffer) {
|
||||
Paragraph::new("Welcome to the Ratatui tabs example!")
|
||||
.block(self.block())
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_soundcloud(block: Block<'static>, area: Rect, buf: &mut Buffer, playlists: Option<CloudPlaylists>) {
|
||||
Paragraph::new("Your playlists from soundcloud:")
|
||||
.block(block)
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
fn render_youtube(self, area: Rect, buf: &mut Buffer) {
|
||||
Paragraph::new("I know, these are some basic changes. But I think you got the main idea.")
|
||||
.block(self.block())
|
||||
.render(area, buf);
|
||||
}
|
||||
|
||||
/// A block surrounding the tab's content
|
||||
fn block(&self) -> Block<'static> {
|
||||
Block::bordered()
|
||||
.border_set(symbols::border::THICK)
|
||||
.padding(Padding::horizontal(1))
|
||||
.border_style(self.palette().c700)
|
||||
}
|
||||
|
||||
pub fn palette(&self) -> tailwind::Palette {
|
||||
match self {
|
||||
Self::Albums => tailwind::INDIGO,
|
||||
Self::Playlists => tailwind::EMERALD,
|
||||
Self::Soundcloud(_) => tailwind::ORANGE,
|
||||
Self::Youtube => tailwind::RED,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn previous(self) -> Self {
|
||||
let current_index = self.clone().to_usize();
|
||||
let previous_index = current_index.saturating_sub(1);
|
||||
Self::from_repr(previous_index).unwrap_or(self)
|
||||
}
|
||||
|
||||
pub fn next(self) -> Self {
|
||||
let current_index = self.clone().to_usize();
|
||||
let next_index = current_index.saturating_add(1);
|
||||
Self::from_repr(next_index).unwrap_or(self)
|
||||
}
|
||||
|
||||
pub fn to_usize(self) -> usize {
|
||||
SelectedTab::iter().enumerate().find(|(_i, el)| el.as_ref() == self.as_ref()).unwrap().0
|
||||
}
|
||||
}
|
44
src/wait_screen.rs
Normal file
44
src/wait_screen.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use ratatui::{style::Stylize, symbols::border, text::{Line, Text}, widgets::{Block, Paragraph, Widget}, Frame};
|
||||
|
||||
use crate::screen::AppScreen;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct WaitScreen {}
|
||||
|
||||
impl AppScreen for WaitScreen {
|
||||
fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame) {
|
||||
let title = Line::from(" Lyrica ".bold());
|
||||
let instructions = Line::from(vec![
|
||||
" Quit ".into(),
|
||||
"<Q> ".red().bold(),
|
||||
]);
|
||||
let block = Block::bordered()
|
||||
.title(title.centered())
|
||||
.title_bottom(instructions.centered())
|
||||
.border_set(border::ROUNDED);
|
||||
|
||||
let counter_text = Text::from(
|
||||
vec![
|
||||
Line::from(
|
||||
vec![
|
||||
"Searching for iPod...".into()
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
let par = Paragraph::new(counter_text)
|
||||
.centered()
|
||||
.block(block);
|
||||
|
||||
frame.render_widget(par, frame.area());
|
||||
}
|
||||
|
||||
fn as_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user