From 1eba31a03ff556f5e8e104892eb33ea8a7c30182 Mon Sep 17 00:00:00 2001 From: structix Date: Sun, 24 Mar 2024 16:22:57 +0100 Subject: [PATCH] Add user interface --- Cargo.toml | 3 + src/main.rs | 20 +++--- src/userinterface.rs | 153 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 src/userinterface.rs diff --git a/Cargo.toml b/Cargo.toml index 9407137..876b52f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.81" +crossterm = "0.27.0" mpris = "2.0.1" +ratatui = "0.26.1" +tui-textarea = "0.4.0" diff --git a/src/main.rs b/src/main.rs index 9e5056f..fd3dffc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,13 @@ mod player; - -use mpris::PlayerFinder; +mod userinterface; fn main() { - println!("Hello, world!"); - let player = PlayerFinder::new() - .expect("Could not connect to D-Bus") - .find_active() - .expect("Could not find any player"); + let player = player::MprisPlayer::new().expect("Could not create player"); - let metadata = player.get_metadata().expect("Could not find metadata"); - if let (Some(title), Some(artists)) = (metadata.title(), metadata.artists()) { - let artist = artists.join(", "); - println!("Current track: {artist} - {title}"); - } + let (usernumber, userrating) = userinterface::get_user_rating().expect("Lala"); + + let track = player + .get_interpret_and_track() + .expect("Could not read track"); + println!("User: {usernumber} with rating: {userrating}: {track}"); } diff --git a/src/userinterface.rs b/src/userinterface.rs new file mode 100644 index 0000000..2d359b7 --- /dev/null +++ b/src/userinterface.rs @@ -0,0 +1,153 @@ +use crossterm::event::{DisableMouseCapture, EnableMouseCapture}; +use crossterm::terminal::{ + disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, +}; +use ratatui::backend::CrosstermBackend; +use ratatui::layout::{Constraint, Layout}; +use ratatui::style::{Color, Style}; +use ratatui::widgets::{Block, Borders}; +use ratatui::Terminal; +use std::io; +use tui_textarea::{Input, Key, TextArea}; + +use anyhow::Result; + +fn validate_user(textarea: &mut TextArea) -> bool { + if let Err(_) = textarea.lines()[0].parse::() { + textarea.set_style(Style::default().fg(Color::LightRed)); + textarea.set_block( + Block::default() + .borders(Borders::ALL) + .title(format!("ERROR: Please enter a valid user number")), + ); + false + } else { + textarea.set_style(Style::default().fg(Color::LightGreen)); + textarea.set_block(Block::default().borders(Borders::ALL).title("OK")); + true + } +} + +fn validate_rating(textarea: &mut TextArea) -> bool { + match textarea.lines()[0].parse::() { + Err(_) => { + textarea.set_style(Style::default().fg(Color::LightRed)); + textarea.set_block( + Block::default() + .borders(Borders::ALL) + .title(format!("ERROR: Rating must be between 1-5.")), + ); + false + } + Ok(value) => { + if value > 0 && value < 6 { + textarea.set_style(Style::default().fg(Color::LightGreen)); + textarea.set_block(Block::default().borders(Borders::ALL).title("OK")); + true + } else { + textarea.set_block( + Block::default() + .borders(Borders::ALL) + .title(format!("ERROR: Rating must be between 1-5.")), + ); + false + } + } + } +} + +pub fn get_user_rating() -> Result<(String, String)> { + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + + enable_raw_mode()?; + crossterm::execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; + let backend = CrosstermBackend::new(stdout); + let mut term = Terminal::new(backend)?; + + let mut textarea = TextArea::default(); + textarea.set_cursor_line_style(Style::default()); + textarea.set_placeholder_text("USER: Enter a valid user number (e.g. 156)"); + let layout = + Layout::default().constraints([Constraint::Length(3), Constraint::Min(1)].as_slice()); + let mut is_valid = validate_user(&mut textarea); + + loop { + term.draw(|f| { + let chunks = layout.split(f.size()); + let widget = textarea.widget(); + f.render_widget(widget, chunks[0]); + })?; + + match crossterm::event::read()?.into() { + //Input { key: Key::Esc, .. } => break, + Input { + key: Key::Enter, .. + } if is_valid => break, + Input { + key: Key::Char('m'), + ctrl: true, + .. + } + | Input { + key: Key::Enter, .. + } => {} + input => { + // TextArea::input returns if the input modified its text + if textarea.input(input) { + is_valid = validate_user(&mut textarea); + } + } + } + } + + let user_number = textarea.lines()[0].clone(); + + let mut textarea = TextArea::default(); + textarea.set_cursor_line_style(Style::default()); + textarea.set_placeholder_text("RATING: Enter a valid rating (between 1-5)"); + let layout = + Layout::default().constraints([Constraint::Length(3), Constraint::Min(1)].as_slice()); + let mut is_valid = validate_rating(&mut textarea); + + loop { + term.draw(|f| { + let chunks = layout.split(f.size()); + let widget = textarea.widget(); + f.render_widget(widget, chunks[0]); + })?; + + match crossterm::event::read()?.into() { + //Input { key: Key::Esc, .. } => break, + Input { + key: Key::Enter, .. + } if is_valid => break, + Input { + key: Key::Char('m'), + ctrl: true, + .. + } + | Input { + key: Key::Enter, .. + } => {} + input => { + // TextArea::input returns if the input modified its text + if textarea.input(input) { + is_valid = validate_rating(&mut textarea); + } + } + } + } + + let user_rating = textarea.lines()[0].clone(); + + disable_raw_mode()?; + crossterm::execute!( + term.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + )?; + term.show_cursor()?; + + Ok((user_number, user_rating)) +}