forked from trinitrix/core
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:
parent
af7c79ac75
commit
7a3bb91ba4
168
src/ui/mod.rs
168
src/ui/mod.rs
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue