use iced::Element; use crate::styles::Theme; use crate::app::Message; use iced::Align; use iced::Length; use iced::widget::text_input::{self, TextInput}; use iced::widget::scrollable::{self, Scrollable}; #[derive(Clone, Debug)] pub struct Lyrics { lines: Vec, scroll_state: scrollable::State, } impl Lyrics { pub fn new() -> Self { let mut lyric = Lyric::new(); lyric.select(); Lyrics { lines: vec![lyric], scroll_state: scrollable::State::new() } } pub fn insert_text(&mut self, text: String) { let mut pieces = text.trim_end() .split('\n') .map(str::trim); let (line_no, current_line) = self.current_line_mut(); current_line.deselect(); current_line.value.push_str(pieces.next().unwrap()); let pieces = pieces .collect::>() .into_iter() .map(str::to_owned) .map(Lyric::new_with_value); let n_pieces = pieces.size_hint().0; self.lines.splice((line_no + 1)..(line_no + 1), pieces); self.lines[line_no + n_pieces].select(); } pub fn update_line(&mut self, new_content: String, line_no: usize) { self.lines[line_no].value = new_content; } pub fn advance_line(&mut self, current_line: usize) { let new_line = current_line + 1; let line = if new_line == self.lines.len() { self.insert_line(new_line, None) } else { self.lines.get_mut(new_line) .expect("Unexpected .advance_line with index beyond # of lines") }; line.select(); self.lines.get_mut(current_line) .unwrap() .deselect(); } pub fn insert_line(&mut self, index: usize, content: Option) -> &mut Lyric { self.lines.insert(index, match content { Some(content) => Lyric::new_with_value(content), None => Lyric::new(), }); self.lines.get_mut(index).unwrap() } pub fn current_line_mut(&mut self) -> (usize, &mut Lyric) { self.lines .iter_mut() .enumerate() .filter(|(_, l)| l.is_selected()) .next() .expect("no line currently selected") } pub fn view(&mut self, theme: Theme) -> Element { let is_sole_line = self.lines.len() == 1; self.lines.iter_mut() .enumerate() .map(|(i, l)| l.view(is_sole_line, i, theme)) .fold(Scrollable::new(&mut self.scroll_state), |s, l| s.push(l)) .width(Length::Fill) .align_items(Align::Center) .into() } } #[derive(Clone, Debug)] pub struct Lyric { state: text_input::State, pub value: String, } impl Lyric { pub fn new() -> Self { Self::new_with_value(String::with_capacity(70)) } pub fn new_with_value(val: String) -> Self { Lyric { state: text_input::State::new(), value: val, } } pub fn view(&mut self, show_placeholder: bool, line_no: usize, theme: Theme) -> Element { let placeholder = if show_placeholder { "Paste some lyrics to get started" } else if self.state.is_focused() { "..." } else { "" }; let size = if self.state.is_focused() { 30 } else { 20 }; TextInput::new( &mut self.state, placeholder, &self.value, move|new_value| Message::LyricChanged { line_no, new_value }, ) .style(theme) .size(size) .width(Length::Units(350)) .on_submit(Message::LineAdvanced(line_no)) .into() } pub fn select(&mut self) { self.state.focus(); self.state.move_cursor_to_end(); } pub fn is_selected(&self) -> bool { self.state.is_focused() } pub fn deselect(&mut self) { self.state.unfocus(); } }