setting_ui.rs (5822B)
1 use crossterm::event::{KeyCode, KeyEvent}; 2 use ratatui::{ 3 Frame, 4 layout::{Constraint, Direction, Layout, Rect}, 5 style::{Color, Modifier, Style}, 6 widgets::{Block, Borders, Clear, List, ListItem}, 7 }; 8 9 use crate::{ 10 app::App, 11 audio::{AudioPlayer, DeviceManager}, 12 }; 13 14 pub fn render(f: &mut Frame, app: &mut App) { 15 let area = f.area(); 16 let settings_items = vec![ 17 ListItem::new(format!("Volume: {:.2}", app.settings.audio().volume())), 18 ListItem::new(format!( 19 "Library Path: {}", 20 app.settings.audio().library_path() 21 )), 22 ListItem::new(format!( 23 "Output Device: {}", 24 app.settings.audio().output_device() 25 )), 26 ]; 27 28 let list = List::new(settings_items) 29 .block( 30 Block::default() 31 .title("Settings") 32 .borders(Borders::ALL) 33 .border_style(Style::default().fg(Color::Yellow)), 34 ) 35 .highlight_style( 36 Style::default() 37 .bg(Color::Blue) 38 .add_modifier(Modifier::BOLD), 39 ) 40 .highlight_symbol(">> "); 41 42 f.render_stateful_widget(list, area, &mut app.state.settings_state); 43 44 if app.state.is_device_dialog_open { 45 render_device_dialog(f, app); 46 } 47 } 48 49 fn render_device_dialog(f: &mut Frame, app: &mut App) { 50 let area = centered_rect(60, 40, f.area()); 51 f.render_widget(Clear, area); 52 53 let items: Vec<ListItem> = app 54 .state 55 .available_devices 56 .iter() 57 .map(|name| ListItem::new(name.as_str())) 58 .collect(); 59 60 let list = List::new(items) 61 .block( 62 Block::default() 63 .title("Select Output Device") 64 .borders(Borders::ALL) 65 .border_style(Style::default().fg(Color::Yellow)), 66 ) 67 .highlight_style( 68 Style::default() 69 .bg(Color::Blue) 70 .add_modifier(Modifier::BOLD), 71 ) 72 .highlight_symbol(">> "); 73 74 f.render_stateful_widget(list, area, &mut app.state.device_dialog_state); 75 } 76 77 fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { 78 let popup_layout = Layout::default() 79 .direction(Direction::Vertical) 80 .constraints([ 81 Constraint::Percentage((100 - percent_y) / 2), 82 Constraint::Percentage(percent_y), 83 Constraint::Percentage((100 - percent_y) / 2), 84 ]) 85 .split(r); 86 87 Layout::default() 88 .direction(Direction::Horizontal) 89 .constraints([ 90 Constraint::Percentage((100 - percent_x) / 2), 91 Constraint::Percentage(percent_x), 92 Constraint::Percentage((100 - percent_x) / 2), 93 ]) 94 .split(popup_layout[1])[1] 95 } 96 97 pub fn handle_key_event(app: &mut App, key: KeyEvent) -> Result<bool, Box<dyn std::error::Error>> { 98 if app.state.is_device_dialog_open { 99 return handle_dialog_key_event(app, key); 100 } 101 102 match key.code { 103 KeyCode::Up => { 104 let i = app.state.settings_state.selected().unwrap_or(0); 105 if i > 0 { 106 app.state.settings_state.select(Some(i - 1)); 107 } 108 } 109 KeyCode::Down => { 110 let i = app.state.settings_state.selected().unwrap_or(0); 111 if i < 2 { 112 app.state.settings_state.select(Some(i + 1)); 113 } 114 } 115 KeyCode::Left => { 116 if app.state.settings_state.selected() == Some(0) { 117 let new_vol = (app.settings.audio().volume() - 0.1).max(0.0); 118 app.settings.audio_mut().set_volume(new_vol); 119 app.audioengine.set_volume(new_vol); 120 app.settings.save()?; 121 } 122 } 123 KeyCode::Right => { 124 if app.state.settings_state.selected() == Some(0) { 125 let new_vol = (app.settings.audio().volume() + 0.1).min(1.0); 126 app.settings.audio_mut().set_volume(new_vol); 127 app.audioengine.set_volume(new_vol); 128 app.settings.save()?; 129 } 130 } 131 KeyCode::Enter => { 132 if app.state.settings_state.selected() == Some(2) { 133 let devices = DeviceManager::list_device_names(); 134 app.state.open_device_dialog(devices); 135 } 136 } 137 _ => {} 138 } 139 Ok(false) 140 } 141 142 fn handle_dialog_key_event( 143 app: &mut App, 144 key: KeyEvent, 145 ) -> Result<bool, Box<dyn std::error::Error>> { 146 match key.code { 147 KeyCode::Up => { 148 let i = app.state.device_dialog_state.selected().unwrap_or(0); 149 if i > 0 { 150 app.state.device_dialog_state.select(Some(i - 1)); 151 } 152 } 153 KeyCode::Down => { 154 let i = app.state.device_dialog_state.selected().unwrap_or(0); 155 if !app.state.available_devices.is_empty() && i < app.state.available_devices.len() - 1 156 { 157 app.state.device_dialog_state.select(Some(i + 1)); 158 } 159 } 160 KeyCode::Enter => { 161 if let Some(i) = app.state.device_dialog_state.selected() { 162 if let Some(device_name) = app.state.available_devices.get(i) { 163 app.settings 164 .audio_mut() 165 .set_output_device(device_name.clone()); 166 app.recreate_audio_engine()?; 167 app.settings.save()?; 168 } 169 } 170 app.state.close_device_dialog(); 171 } 172 KeyCode::Esc => { 173 app.state.close_device_dialog(); 174 } 175 _ => {} 176 } 177 Ok(false) 178 }