Render entire UI using widgets

This commit is contained in:
Joscha 2022-07-20 22:56:00 +02:00
parent 8b3166c6d7
commit 3bbe52b797
2 changed files with 28 additions and 20 deletions

View file

@ -14,7 +14,6 @@ use parking_lot::FairMutex;
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::task; use tokio::task;
use toss::frame::Frame;
use toss::terminal::Terminal; use toss::terminal::Terminal;
use crate::logger::{LogMsg, Logger}; use crate::logger::{LogMsg, Logger};
@ -22,7 +21,7 @@ use crate::vault::Vault;
use self::chat::ChatState; use self::chat::ChatState;
use self::rooms::Rooms; use self::rooms::Rooms;
use self::widgets::Widget; use self::widgets::BoxedWidget;
#[derive(Debug)] #[derive(Debug)]
pub enum UiEvent { pub enum UiEvent {
@ -121,26 +120,32 @@ impl Ui {
mut event_rx: UnboundedReceiver<UiEvent>, mut event_rx: UnboundedReceiver<UiEvent>,
crossterm_lock: Arc<FairMutex<()>>, crossterm_lock: Arc<FairMutex<()>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
loop { // Initial render so we don't show a blank screen until the first event
// 1. Render current state terminal.autoresize()?;
terminal.autoresize()?; terminal.frame().reset();
self.render(terminal.frame()).await?; self.widget().await.render(terminal.frame()).await;
terminal.present()?; terminal.present()?;
// 2. Measure widths if required loop {
// 1. Measure grapheme widths if required
if terminal.measuring_required() { if terminal.measuring_required() {
let _guard = crossterm_lock.lock(); let _guard = crossterm_lock.lock();
terminal.measure_widths()?; terminal.measure_widths()?;
self.event_tx.send(UiEvent::Redraw)?; self.event_tx.send(UiEvent::Redraw)?;
} }
// 3. Handle events (in batches) // 2. Handle events (in batches)
let mut event = match event_rx.recv().await { let mut event = match event_rx.recv().await {
Some(event) => event, Some(event) => event,
None => return Ok(()), None => return Ok(()),
}; };
terminal.autoresize()?; terminal.autoresize()?;
loop { loop {
// Render in-between events so the next event is handled in an
// up-to-date state. The results of these intermediate renders
// will be thrown away before the final render.
self.widget().await.render(terminal.frame()).await;
let result = match event { let result = match event {
UiEvent::Redraw => EventHandleResult::Continue, UiEvent::Redraw => EventHandleResult::Continue,
UiEvent::Term(Event::Key(event)) => { UiEvent::Term(Event::Key(event)) => {
@ -160,15 +165,19 @@ impl Ui {
Err(TryRecvError::Disconnected) => return Ok(()), Err(TryRecvError::Disconnected) => return Ok(()),
}; };
} }
// 3. Render and present final state
terminal.frame().reset();
self.widget().await.render(terminal.frame()).await;
terminal.present()?;
} }
} }
async fn render(&mut self, frame: &mut Frame) -> anyhow::Result<()> { async fn widget(&mut self) -> BoxedWidget {
match self.mode { match self.mode {
Mode::Main => self.rooms.render(frame).await, Mode::Main => self.rooms.widget().await,
Mode::Log => Box::new(self.log_chat.widget()).render(frame).await, Mode::Log => self.log_chat.widget().into(),
} }
Ok(())
} }
async fn handle_key_event( async fn handle_key_event(

View file

@ -6,7 +6,6 @@ use crossterm::event::{KeyCode, KeyEvent};
use crossterm::style::{ContentStyle, Stylize}; use crossterm::style::{ContentStyle, Stylize};
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use toss::frame::Frame;
use toss::styled::Styled; use toss::styled::Styled;
use toss::terminal::Terminal; use toss::terminal::Terminal;
@ -18,7 +17,7 @@ use super::room::EuphRoom;
use super::widgets::background::Background; use super::widgets::background::Background;
use super::widgets::list::{List, ListState}; use super::widgets::list::{List, ListState};
use super::widgets::text::Text; use super::widgets::text::Text;
use super::widgets::Widget; use super::widgets::BoxedWidget;
use super::{util, UiEvent}; use super::{util, UiEvent};
pub struct Rooms { pub struct Rooms {
@ -67,14 +66,14 @@ impl Rooms {
rooms rooms
} }
pub async fn render(&mut self, frame: &mut Frame) { pub async fn widget(&mut self) -> BoxedWidget {
if let Some(room) = &self.focus { if let Some(room) = &self.focus {
let actual_room = self.euph_rooms.entry(room.clone()).or_insert_with(|| { let actual_room = self.euph_rooms.entry(room.clone()).or_insert_with(|| {
EuphRoom::new(self.vault.euph(room.clone()), self.ui_event_tx.clone()) EuphRoom::new(self.vault.euph(room.clone()), self.ui_event_tx.clone())
}); });
actual_room.widget().await.render(frame).await; actual_room.widget().await
} else { } else {
self.render_rooms(frame).await; self.rooms_widget().await
} }
} }
@ -148,11 +147,11 @@ impl Rooms {
} }
} }
async fn render_rooms(&mut self, frame: &mut Frame) { async fn rooms_widget(&mut self) -> BoxedWidget {
let rooms = self.stabilize_rooms().await; let rooms = self.stabilize_rooms().await;
let mut list = self.list.list().focus(true); let mut list = self.list.list().focus(true);
self.render_rows(&mut list, rooms).await; self.render_rows(&mut list, rooms).await;
Box::new(list).render(frame).await; list.into()
} }
pub async fn handle_key_event( pub async fn handle_key_event(