diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 0059172..ace147d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -41,6 +41,7 @@ pub struct UI<'a> { message_compose: TextArea<'a>, } + fn terminal_prepare() -> Result { enable_raw_mode()?; let mut stdout = io::stdout(); @@ -48,71 +49,6 @@ fn terminal_prepare() -> Result { return Ok(stdout); } -fn draw_main_status_block(frame: &mut Frame>, area: Rect, app: &App) { - let account = app.accounts_manager.current().expect("failed to resolve current account"); - - let mut content = Text::styled(account.name(), Style::default().add_modifier(Modifier::BOLD)); - content.extend(Text::styled(account.user_id(), Style::default())); - content.extend(Text::styled("settings", Style::default().fg(Color::LightMagenta).add_modifier(Modifier::ITALIC | Modifier::UNDERLINED))); - - let panel = Paragraph::new(content) - .block(Block::default() - .title("Status") - .borders(Borders::ALL)) - .alignment(Alignment::Left); - - frame.render_widget(panel, area); -} - -fn draw_main_rooms_block(frame: &mut Frame>, area: Rect) { - let panel = Block::default() - .title("Rooms") - .borders(Borders::ALL); - - frame.render_widget(panel, area); -} - -fn draw_main_messages_block(frame: &mut Frame>, area: Rect, app: &App) { - let messages = match app.room() { - None => return, - Some(r) => { - r.messages - .iter() - .rev() - .map(|msg| { - Spans::from(vec![ - Span::styled(&msg.author, Style::default().fg(Color::Cyan)), - Span::styled(": ", Style::default().fg(Color::Cyan)), - Span::styled(&msg.message, Style::default().fg(Color::White)), - ]) - }) - .collect::>() - }, - }; - - let panel = Paragraph::new(messages) - .block(Block::default() - .title("Messages") - .borders(Borders::ALL)) - .alignment(Alignment::Left) - .wrap(Wrap {trim: false}); - - - frame.render_widget(panel, area); -} - -fn draw_main_room_info_block(frame: &mut Frame>, area: Rect) { - let panel = Block::default() - .title("Room Info") - .borders(Borders::ALL); - - frame.render_widget(panel, area); -} - -fn draw_main_message_compose_block(frame: &mut Frame>, area: Rect, textarea: &TextArea) { - frame.render_widget(textarea.widget(), area); -} - fn textarea_activate(textarea: &mut TextArea) { textarea.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED)); textarea.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED)); @@ -135,6 +71,15 @@ fn textarea_inactivate(textarea: &mut TextArea) { ); } +fn cycle_main_input_position(input_position: &MainInputPosition) -> MainInputPosition { + match input_position { + MainInputPosition::Status => MainInputPosition::Rooms, + MainInputPosition::Rooms => MainInputPosition::Messages, + MainInputPosition::Messages => MainInputPosition::MessageCompose, + MainInputPosition::MessageCompose => MainInputPosition::RoomInfo, + MainInputPosition::RoomInfo => MainInputPosition::Status, + } +} impl UI<'_> { pub fn new() -> Self { @@ -182,12 +127,90 @@ impl UI<'_> { .constraints([Constraint::Min(4)].as_ref()) .split(main_chunks[2]); + // collect data to render + let account = app.accounts_manager.current().expect("failed to resolve current account"); + + let mut status_content = Text::styled(account.name(), Style::default().add_modifier(Modifier::BOLD)); + status_content.extend(Text::styled(account.user_id(), Style::default())); + status_content.extend(Text::styled("settings", Style::default().fg(Color::LightMagenta).add_modifier(Modifier::ITALIC | Modifier::UNDERLINED))); + + let messages_content = match app.room() { + None => { + vec![Spans::from(Span::styled("No room selected!", Style::default().fg(Color::Magenta)))] + }, + Some(r) => { + r.messages + .iter() + .rev() + .map(|msg| { + Spans::from(vec![ + Span::styled(&msg.author, Style::default().fg(Color::Cyan)), + Span::styled(": ", Style::default().fg(Color::Cyan)), + Span::styled(&msg.message, Style::default().fg(Color::White)), + ]) + }) + .collect::>() + }, + }; + + // calculate to widgets colors, based of which widget is currently selected + let colors = match self.input_position { + MainInputPosition::Status => { + textarea_inactivate(&mut self.message_compose); + vec![Color::White, Color::DarkGray, Color::DarkGray, Color::DarkGray, Color::DarkGray] + }, + MainInputPosition::Rooms => { + textarea_inactivate(&mut self.message_compose); + vec![Color::DarkGray, Color::White, Color::DarkGray, Color::DarkGray, Color::DarkGray] + }, + MainInputPosition::Messages => { + textarea_inactivate(&mut self.message_compose); + vec![Color::DarkGray, Color::DarkGray, Color::White, Color::DarkGray, Color::DarkGray] + }, + MainInputPosition::MessageCompose => { + textarea_activate(&mut self.message_compose); + vec![Color::DarkGray, Color::DarkGray, Color::DarkGray, Color::DarkGray, Color::DarkGray] + }, + MainInputPosition::RoomInfo => { + textarea_inactivate(&mut self.message_compose); + vec![Color::DarkGray, Color::DarkGray, Color::DarkGray, Color::DarkGray, Color::White] + }, + }; + + // initiate the widgets + let status = Paragraph::new(status_content) + .block(Block::default() + .title("Status") + .borders(Borders::ALL) + .style(Style::default().fg(colors[MainInputPosition::Status as usize]))) + .alignment(Alignment::Left); + + let rooms = Block::default() + .title("Rooms") + .borders(Borders::ALL) + .style(Style::default().fg(colors[MainInputPosition::Rooms as usize])); + + let messages = Paragraph::new(messages_content) + .block(Block::default() + .title("Messages") + .borders(Borders::ALL) + .style(Style::default().fg(colors[MainInputPosition::Messages as usize]))) + .alignment(Alignment::Left) + .wrap(Wrap {trim: false}); + + let room_info = Block::default() + .title("Room Info") + .borders(Borders::ALL) + .style(Style::default().fg(colors[MainInputPosition::RoomInfo as usize])); + + + // render the widgets self.terminal.draw(|frame| { - draw_main_status_block(frame, left_chunks[0], app); - draw_main_rooms_block(frame, left_chunks[1]); - draw_main_messages_block(frame, middle_chunks[0], app); - draw_main_message_compose_block(frame, middle_chunks[1], &self.message_compose); - draw_main_room_info_block(frame, right_chunks[0]); + frame.render_widget(status, left_chunks[0]); + frame.render_widget(rooms, left_chunks[1]); + frame.render_widget(messages, middle_chunks[0]); + frame.render_widget(self.message_compose.widget(), middle_chunks[1]); + frame.render_widget(room_info, right_chunks[0]); })?; Ok(()) @@ -196,6 +219,8 @@ impl UI<'_> { pub async fn main(&mut self, app: &mut App) -> Result<()> { textarea_activate(&mut self.message_compose); + self.terminal.clear().expect("failed to clear screen"); + loop { self.main_update(app)?; @@ -205,8 +230,7 @@ impl UI<'_> { key: Key::Tab, .. } => { - // TODO: calc input_position - self.input_position = MainInputPosition::MessageCompose; + self.input_position = cycle_main_input_position(&self.input_position); } input => { match self.input_position {