1
0
Fork 0

feature (UI - main): built a simple not yet very efficient rendering infrastructure that is capable of cycling through the planes

This commit is contained in:
antifallobst 2023-06-29 16:33:40 +02:00
parent af7c79ac75
commit 7a3bb91ba4
1 changed files with 96 additions and 72 deletions

View File

@ -41,6 +41,7 @@ pub struct UI<'a> {
message_compose: TextArea<'a>,
}
fn terminal_prepare() -> Result<Stdout> {
enable_raw_mode()?;
let mut stdout = io::stdout();
@ -48,71 +49,6 @@ fn terminal_prepare() -> Result<Stdout> {
return Ok(stdout);
}
fn draw_main_status_block(frame: &mut Frame<CrosstermBackend<Stdout>>, 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<CrosstermBackend<Stdout>>, area: Rect) {
let panel = Block::default()
.title("Rooms")
.borders(Borders::ALL);
frame.render_widget(panel, area);
}
fn draw_main_messages_block(frame: &mut Frame<CrosstermBackend<Stdout>>, 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::<Vec<_>>()
},
};
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<CrosstermBackend<Stdout>>, 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<CrosstermBackend<Stdout>>, 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::<Vec<_>>()
},
};
// 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 {