From d8d3e64776acf3be7cafe73f8c23a3a6ad61c8b9 Mon Sep 17 00:00:00 2001 From: Joscha Date: Wed, 12 Apr 2023 20:17:38 +0200 Subject: [PATCH] Migrate room to AsyncWidget --- src/ui/euph/room.rs | 152 +++++++++++++++++++++++++------------------- src/ui/rooms.rs | 8 +-- 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/ui/euph/room.rs b/src/ui/euph/room.rs index 75d1f11..9f9786d 100644 --- a/src/ui/euph/room.rs +++ b/src/ui/euph/room.rs @@ -8,22 +8,18 @@ use euphoxide::conn::{self, Joined, Joining, SessionInfo}; use parking_lot::FairMutex; use tokio::sync::oneshot::error::TryRecvError; use tokio::sync::{mpsc, oneshot}; -use toss::{Style, Styled, Terminal}; +use toss::widgets::{BoxedAsync, Join2, Layer, Text}; +use toss::{AsyncWidget, Style, Styled, Terminal, WidgetExt}; use crate::config; use crate::euph; use crate::macros::logging_unwrap; use crate::ui::chat::{ChatState, Reaction}; use crate::ui::input::{key, InputEvent, KeyBindingsList}; -use crate::ui::widgets::border::Border; -use crate::ui::widgets::editor::EditorState; -use crate::ui::widgets::join::{HJoin, Segment, VJoin}; -use crate::ui::widgets::layer::Layer; -use crate::ui::widgets::list::ListState; -use crate::ui::widgets::padding::Padding; -use crate::ui::widgets::text::Text; -use crate::ui::widgets::BoxedWidget; -use crate::ui::{util, UiEvent}; +use crate::ui::widgets::editor::EditorState as OldEditorState; +use crate::ui::widgets::list::ListState as OldListState; +use crate::ui::widgets::WidgetWrapper; +use crate::ui::{util, UiError, UiEvent}; use crate::vault::EuphRoomVault; use super::account::{self, AccountUiState}; @@ -31,7 +27,7 @@ use super::links::{self, LinksState}; use super::popup::RoomPopup; use super::{auth, inspect, nick, nick_list}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Focus { Chat, NickList, @@ -40,14 +36,16 @@ enum Focus { #[allow(clippy::large_enum_variant)] enum State { Normal, - Auth(EditorState), - Nick(EditorState), + Auth(OldEditorState), + Nick(OldEditorState), Account(AccountUiState), Links(LinksState), InspectMessage(Message), InspectSession(SessionInfo), } +type EuphChatState = ChatState; + pub struct EuphRoom { server_config: ServerConfig, config: config::EuphRoom, @@ -59,10 +57,10 @@ pub struct EuphRoom { state: State, popups: VecDeque, - chat: ChatState, + chat: EuphChatState, last_msg_sent: Option>, - nick_list: ListState, + nick_list: OldListState, } impl EuphRoom { @@ -82,7 +80,7 @@ impl EuphRoom { popups: VecDeque::new(), chat: ChatState::new(vault), last_msg_sent: None, - nick_list: ListState::new(), + nick_list: OldListState::new(), } } @@ -205,77 +203,97 @@ impl EuphRoom { self.stabilize_state(); } - pub async fn widget(&mut self) -> BoxedWidget { + pub async fn widget(&mut self) -> BoxedAsync<'_, UiError> { self.stabilize().await; - let room_state = self.room_state(); + let room_state = self.room.as_ref().map(|room| room.state()); + let status_widget = self.status_widget(room_state).await; let chat = if let Some(euph::State::Connected(_, conn::State::Joined(joined))) = room_state { - self.widget_with_nick_list(room_state, joined).await + Self::widget_with_nick_list( + &mut self.chat, + status_widget, + &mut self.nick_list, + joined, + self.focus, + ) } else { - self.widget_without_nick_list(room_state).await + Self::widget_without_nick_list(&mut self.chat, status_widget) }; let mut layers = vec![chat]; match &self.state { State::Normal => {} - State::Auth(editor) => layers.push(auth::widget(editor)), - State::Nick(editor) => layers.push(nick::widget(editor)), - State::Account(account) => layers.push(account.widget()), - State::Links(links) => layers.push(links.widget()), - State::InspectMessage(message) => layers.push(inspect::message_widget(message)), - State::InspectSession(session) => layers.push(inspect::session_widget(session)), + State::Auth(editor) => { + layers.push(WidgetWrapper::new(auth::widget(editor)).boxed_async()) + } + State::Nick(editor) => { + layers.push(WidgetWrapper::new(nick::widget(editor)).boxed_async()) + } + State::Account(account) => { + layers.push(WidgetWrapper::new(account.widget()).boxed_async()) + } + State::Links(links) => layers.push(WidgetWrapper::new(links.widget()).boxed_async()), + State::InspectMessage(message) => { + layers.push(WidgetWrapper::new(inspect::message_widget(message)).boxed_async()) + } + State::InspectSession(session) => { + layers.push(WidgetWrapper::new(inspect::session_widget(session)).boxed_async()) + } } for popup in &self.popups { - layers.push(popup.widget()); + layers.push(WidgetWrapper::new(popup.widget()).boxed_async()); } - Layer::new(layers).into() + Layer::new(layers).boxed_async() } - async fn widget_without_nick_list(&self, state: Option<&euph::State>) -> BoxedWidget { - VJoin::new(vec![ - Segment::new(Border::new( - Padding::new(self.status_widget(state).await).horizontal(1), - )), - // TODO Use last known nick? - Segment::new(self.chat.widget(String::new(), true)).expanding(true), - ]) - .into() + fn widget_without_nick_list( + chat: &mut EuphChatState, + status_widget: impl AsyncWidget + Send + Sync + 'static, + ) -> BoxedAsync<'_, UiError> { + let chat_widget = WidgetWrapper::new(chat.widget(String::new(), true)); + + Join2::vertical( + status_widget.segment().with_fixed(true), + chat_widget.segment(), + ) + .boxed_async() } - async fn widget_with_nick_list( - &self, - state: Option<&euph::State>, + fn widget_with_nick_list<'a>( + chat: &'a mut EuphChatState, + status_widget: impl AsyncWidget + Send + Sync + 'static, + nick_list: &mut OldListState, joined: &Joined, - ) -> BoxedWidget { - HJoin::new(vec![ - Segment::new(VJoin::new(vec![ - Segment::new(Border::new( - Padding::new(self.status_widget(state).await).horizontal(1), - )), - Segment::new( - self.chat - .widget(joined.session.name.clone(), self.focus == Focus::Chat), - ) - .expanding(true), - ])) - .expanding(true), - Segment::new(Border::new( - Padding::new(nick_list::widget( - &self.nick_list, - joined, - self.focus == Focus::NickList, - )) - .right(1), - )), - ]) - .into() + focus: Focus, + ) -> BoxedAsync<'a, UiError> { + let nick_list_widget = WidgetWrapper::new(nick_list::widget( + nick_list, + joined, + focus == Focus::NickList, + )) + .padding() + .with_right(1) + .border(); + + let chat_widget = + WidgetWrapper::new(chat.widget(joined.session.name.clone(), focus == Focus::Chat)); + + Join2::horizontal( + Join2::vertical( + status_widget.segment().with_fixed(true), + chat_widget.segment(), + ) + .segment(), + nick_list_widget.segment().with_fixed(true), + ) + .boxed_async() } - async fn status_widget(&self, state: Option<&euph::State>) -> BoxedWidget { + async fn status_widget(&self, state: Option<&euph::State>) -> BoxedAsync<'static, UiError> { let room_style = Style::new().bold().blue(); let mut info = Styled::new(format!("&{}", self.name()), room_style); @@ -308,7 +326,11 @@ impl EuphRoom { .then_plain(")"); } - Text::new(info).into() + Text::new(info) + .padding() + .with_horizontal(1) + .border() + .boxed_async() } async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList) { diff --git a/src/ui/rooms.rs b/src/ui/rooms.rs index 3be809f..348dd5a 100644 --- a/src/ui/rooms.rs +++ b/src/ui/rooms.rs @@ -18,7 +18,6 @@ use crate::vault::Vault; use super::euph::room::EuphRoom; use super::input::{key, InputEvent, KeyBindingsList}; -use super::widgets::WidgetWrapper; use super::widgets2::{List, ListState, Popup}; use super::{util2, UiError, UiEvent}; @@ -176,14 +175,13 @@ impl Rooms { Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order).await } - State::ShowRoom(name) => WidgetWrapper::new( + State::ShowRoom(name) => { self.euph_rooms .get_mut(name) .expect("room exists after stabilization") .widget() - .await, - ) - .boxed_async(), + .await + } State::Connect(editor) => { Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order)