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>,
|
message_compose: TextArea<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn terminal_prepare() -> Result<Stdout> {
|
fn terminal_prepare() -> Result<Stdout> {
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
|
@ -48,71 +49,6 @@ fn terminal_prepare() -> Result<Stdout> {
|
||||||
return Ok(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) {
|
fn textarea_activate(textarea: &mut TextArea) {
|
||||||
textarea.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
textarea.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
||||||
textarea.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
|
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<'_> {
|
impl UI<'_> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -182,12 +127,90 @@ impl UI<'_> {
|
||||||
.constraints([Constraint::Min(4)].as_ref())
|
.constraints([Constraint::Min(4)].as_ref())
|
||||||
.split(main_chunks[2]);
|
.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| {
|
self.terminal.draw(|frame| {
|
||||||
draw_main_status_block(frame, left_chunks[0], app);
|
frame.render_widget(status, left_chunks[0]);
|
||||||
draw_main_rooms_block(frame, left_chunks[1]);
|
frame.render_widget(rooms, left_chunks[1]);
|
||||||
draw_main_messages_block(frame, middle_chunks[0], app);
|
frame.render_widget(messages, middle_chunks[0]);
|
||||||
draw_main_message_compose_block(frame, middle_chunks[1], &self.message_compose);
|
frame.render_widget(self.message_compose.widget(), middle_chunks[1]);
|
||||||
draw_main_room_info_block(frame, right_chunks[0]);
|
frame.render_widget(room_info, right_chunks[0]);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -196,6 +219,8 @@ impl UI<'_> {
|
||||||
pub async fn main(&mut self, app: &mut App) -> Result<()> {
|
pub async fn main(&mut self, app: &mut App) -> Result<()> {
|
||||||
textarea_activate(&mut self.message_compose);
|
textarea_activate(&mut self.message_compose);
|
||||||
|
|
||||||
|
self.terminal.clear().expect("failed to clear screen");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
self.main_update(app)?;
|
self.main_update(app)?;
|
||||||
|
|
||||||
|
@ -205,8 +230,7 @@ impl UI<'_> {
|
||||||
key: Key::Tab,
|
key: Key::Tab,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
// TODO: calc input_position
|
self.input_position = cycle_main_input_position(&self.input_position);
|
||||||
self.input_position = MainInputPosition::MessageCompose;
|
|
||||||
}
|
}
|
||||||
input => {
|
input => {
|
||||||
match self.input_position {
|
match self.input_position {
|
||||||
|
|
Loading…
Reference in New Issue