Update toss and remove more async

This commit is contained in:
Joscha 2023-04-17 20:36:04 +02:00
parent a638caadcb
commit ade7be594e
16 changed files with 109 additions and 116 deletions

2
Cargo.lock generated
View file

@ -1313,7 +1313,7 @@ dependencies = [
[[package]] [[package]]
name = "toss" name = "toss"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/Garmelon/toss.git?rev=57788a9dd9688bdf3e59bf7366ba6276fc660715#57788a9dd9688bdf3e59bf7366ba6276fc660715" source = "git+https://github.com/Garmelon/toss.git?rev=f414db40d526295c74cbcae6c3d194088da8f1d9#f414db40d526295c74cbcae6c3d194088da8f1d9"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"crossterm", "crossterm",

View file

@ -43,7 +43,7 @@ features = ["bot"]
[dependencies.toss] [dependencies.toss]
git = "https://github.com/Garmelon/toss.git" git = "https://github.com/Garmelon/toss.git"
rev = "57788a9dd9688bdf3e59bf7366ba6276fc660715" rev = "f414db40d526295c74cbcae6c3d194088da8f1d9"
# [patch."https://github.com/Garmelon/toss.git"] # [patch."https://github.com/Garmelon/toss.git"]
# toss = { path = "../toss/" } # toss = { path = "../toss/" }

View file

@ -209,6 +209,7 @@ impl Ui {
key_bindings_list key_bindings_list
.widget(list_state) .widget(list_state)
.desync()
.above(widget) .above(widget)
.boxed_async() .boxed_async()
} else { } else {

View file

@ -12,7 +12,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use toss::widgets::EditorState; use toss::widgets::EditorState;
use toss::{AsyncWidget, Frame, Pos, Size, Terminal, WidthDb}; use toss::{AsyncWidget, Frame, Pos, Size, Terminal, WidgetExt, WidthDb};
use crate::store::{Msg, MsgStore}; use crate::store::{Msg, MsgStore};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
@ -488,7 +488,7 @@ where
for (range, block) in renderer.into_visible_blocks() { for (range, block) in renderer.into_visible_blocks() {
let widget = block.into_widget(); let widget = block.into_widget();
frame.push(Pos::new(0, range.top), widget.size()); frame.push(Pos::new(0, range.top), widget.size());
widget.draw(frame).await.infallible(); widget.desync().draw(frame).await.infallible();
frame.pop(); frame.pop();
} }

View file

@ -1,8 +1,8 @@
use crossterm::style::Stylize; use crossterm::style::Stylize;
use euphoxide::api::PersonalAccountView; use euphoxide::api::PersonalAccountView;
use euphoxide::conn; use euphoxide::conn;
use toss::widgets::{BoxedAsync, EditorState, Empty, Join3, Join4, Text}; use toss::widgets::{EditorState, Empty, Join3, Join4, Text};
use toss::{Style, Terminal, WidgetExt}; use toss::{Style, Terminal, Widget, WidgetExt};
use crate::euph::{self, Room}; use crate::euph::{self, Room};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
@ -30,7 +30,7 @@ impl LoggedOut {
} }
} }
fn widget(&mut self) -> BoxedAsync<'_, UiError> { fn widget(&mut self) -> impl Widget<UiError> + '_ {
let bold = Style::new().bold(); let bold = Style::new().bold();
Join4::vertical( Join4::vertical(
Text::new(("Not logged in", bold.yellow())).segment(), Text::new(("Not logged in", bold.yellow())).segment(),
@ -57,14 +57,13 @@ impl LoggedOut {
) )
.segment(), .segment(),
) )
.boxed_async()
} }
} }
pub struct LoggedIn(PersonalAccountView); pub struct LoggedIn(PersonalAccountView);
impl LoggedIn { impl LoggedIn {
fn widget(&self) -> BoxedAsync<'_, UiError> { fn widget(&self) -> impl Widget<UiError> {
let bold = Style::new().bold(); let bold = Style::new().bold();
Join3::vertical( Join3::vertical(
Text::new(("Logged in", bold.green())).segment(), Text::new(("Logged in", bold.green())).segment(),
@ -78,7 +77,6 @@ impl LoggedIn {
) )
.segment(), .segment(),
) )
.boxed_async()
} }
} }
@ -112,15 +110,15 @@ impl AccountUiState {
} }
} }
pub fn widget(&mut self) -> BoxedAsync<'_, UiError> { pub fn widget(&mut self) -> impl Widget<UiError> + '_ {
let inner = match self { let inner = match self {
Self::LoggedOut(logged_out) => logged_out.widget(), Self::LoggedOut(logged_out) => logged_out.widget().first2(),
Self::LoggedIn(logged_in) => logged_in.widget(), Self::LoggedIn(logged_in) => logged_in.widget().second2(),
} }
.resize() .resize()
.with_min_width(40); .with_min_width(40);
Popup::new(inner, "Account").boxed_async() Popup::new(inner, "Account")
} }
pub fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { pub fn list_key_bindings(&self, bindings: &mut KeyBindingsList) {

View file

@ -1,5 +1,5 @@
use toss::widgets::{BoxedAsync, EditorState}; use toss::widgets::EditorState;
use toss::{Terminal, WidgetExt}; use toss::{Terminal, Widget};
use crate::euph::Room; use crate::euph::Room;
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
@ -10,12 +10,11 @@ pub fn new() -> EditorState {
EditorState::new() EditorState::new()
} }
pub fn widget(editor: &mut EditorState) -> BoxedAsync<'_, UiError> { pub fn widget(editor: &mut EditorState) -> impl Widget<UiError> + '_ {
Popup::new( Popup::new(
editor.widget().with_hidden_default_placeholder(), editor.widget().with_hidden_default_placeholder(),
"Enter password", "Enter password",
) )
.boxed_async()
} }
pub fn list_key_bindings(bindings: &mut KeyBindingsList) { pub fn list_key_bindings(bindings: &mut KeyBindingsList) {

View file

@ -1,8 +1,8 @@
use crossterm::style::Stylize; use crossterm::style::Stylize;
use euphoxide::api::{Message, NickEvent, SessionView}; use euphoxide::api::{Message, NickEvent, SessionView};
use euphoxide::conn::SessionInfo; use euphoxide::conn::SessionInfo;
use toss::widgets::{BoxedAsync, Text}; use toss::widgets::Text;
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, Widget};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
use crate::ui::widgets::Popup; use crate::ui::widgets::Popup;
@ -88,7 +88,7 @@ fn message_lines(mut text: Styled, msg: &Message) -> Styled {
text text
} }
pub fn session_widget(session: &SessionInfo) -> BoxedAsync<'static, UiError> { pub fn session_widget(session: &SessionInfo) -> impl Widget<UiError> {
let heading_style = Style::new().bold(); let heading_style = Style::new().bold();
let text = match session { let text = match session {
@ -102,10 +102,10 @@ pub fn session_widget(session: &SessionInfo) -> BoxedAsync<'static, UiError> {
} }
}; };
Popup::new(Text::new(text), "Inspect session").boxed_async() Popup::new(Text::new(text), "Inspect session")
} }
pub fn message_widget(msg: &Message) -> BoxedAsync<'static, UiError> { pub fn message_widget(msg: &Message) -> impl Widget<UiError> {
let heading_style = Style::new().bold(); let heading_style = Style::new().bold();
let mut text = Styled::new("Message", heading_style).then_plain("\n"); let mut text = Styled::new("Message", heading_style).then_plain("\n");
@ -119,7 +119,7 @@ pub fn message_widget(msg: &Message) -> BoxedAsync<'static, UiError> {
text = session_view_lines(text, &msg.sender); text = session_view_lines(text, &msg.sender);
Popup::new(Text::new(text), "Inspect message").boxed_async() Popup::new(Text::new(text), "Inspect message")
} }
pub fn list_key_bindings(bindings: &mut KeyBindingsList) { pub fn list_key_bindings(bindings: &mut KeyBindingsList) {

View file

@ -2,8 +2,8 @@ use std::io;
use crossterm::style::Stylize; use crossterm::style::Stylize;
use linkify::{LinkFinder, LinkKind}; use linkify::{LinkFinder, LinkKind};
use toss::widgets::{BoxedAsync, Text}; use toss::widgets::Text;
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, Widget};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
use crate::ui::widgets::{ListBuilder, ListState, Popup}; use crate::ui::widgets::{ListBuilder, ListState, Popup};
@ -38,7 +38,7 @@ impl LinksState {
} }
} }
pub fn widget(&mut self) -> BoxedAsync<'_, UiError> { pub fn widget(&mut self) -> impl Widget<UiError> + '_ {
let style_selected = Style::new().black().on_white(); let style_selected = Style::new().black().on_white();
let mut list_builder = ListBuilder::new(); let mut list_builder = ListBuilder::new();
@ -74,7 +74,7 @@ impl LinksState {
} }
} }
Popup::new(list_builder.build(&mut self.list), "Links").boxed_async() Popup::new(list_builder.build(&mut self.list), "Links")
} }
fn open_link_by_id(&self, id: usize) -> EventResult { fn open_link_by_id(&self, id: usize) -> EventResult {

View file

@ -1,6 +1,6 @@
use euphoxide::conn::Joined; use euphoxide::conn::Joined;
use toss::widgets::{BoxedAsync, EditorState}; use toss::widgets::EditorState;
use toss::{Style, Terminal, WidgetExt}; use toss::{Style, Terminal, Widget};
use crate::euph::{self, Room}; use crate::euph::{self, Room};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
@ -11,12 +11,12 @@ pub fn new(joined: Joined) -> EditorState {
EditorState::with_initial_text(joined.session.name) EditorState::with_initial_text(joined.session.name)
} }
pub fn widget(editor: &mut EditorState) -> BoxedAsync<'_, UiError> { pub fn widget(editor: &mut EditorState) -> impl Widget<UiError> + '_ {
let inner = editor let inner = editor
.widget() .widget()
.with_highlight(|s| euph::style_nick_exact(s, Style::new())); .with_highlight(|s| euph::style_nick_exact(s, Style::new()));
Popup::new(inner, "Choose nick").boxed_async() Popup::new(inner, "Choose nick")
} }
fn nick_char(c: char) -> bool { fn nick_char(c: char) -> bool {

View file

@ -3,8 +3,8 @@ use std::iter;
use crossterm::style::{Color, Stylize}; use crossterm::style::{Color, Stylize};
use euphoxide::api::{NickEvent, SessionId, SessionType, SessionView, UserId}; use euphoxide::api::{NickEvent, SessionId, SessionType, SessionView, UserId};
use euphoxide::conn::{Joined, SessionInfo}; use euphoxide::conn::{Joined, SessionInfo};
use toss::widgets::{BoxedAsync, Empty, Text}; use toss::widgets::{Background, Text};
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, Widget, WidgetExt};
use crate::euph; use crate::euph;
use crate::ui::widgets::{ListBuilder, ListState}; use crate::ui::widgets::{ListBuilder, ListState};
@ -14,10 +14,10 @@ pub fn widget<'a>(
list: &'a mut ListState<SessionId>, list: &'a mut ListState<SessionId>,
joined: &Joined, joined: &Joined,
focused: bool, focused: bool,
) -> BoxedAsync<'a, UiError> { ) -> impl Widget<UiError> + 'a {
let mut list_builder = ListBuilder::new(); let mut list_builder = ListBuilder::new();
render_rows(&mut list_builder, joined, focused); render_rows(&mut list_builder, joined, focused);
list_builder.build(list).boxed_async() list_builder.build(list)
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
@ -59,7 +59,7 @@ impl HalfSession {
} }
fn render_rows( fn render_rows(
list_builder: &mut ListBuilder<'_, SessionId, BoxedAsync<'static, UiError>>, list_builder: &mut ListBuilder<'_, SessionId, Background<Text>>,
joined: &Joined, joined: &Joined,
focused: bool, focused: bool,
) { ) {
@ -94,7 +94,7 @@ fn render_rows(
} }
fn render_section( fn render_section(
list_builder: &mut ListBuilder<'_, SessionId, BoxedAsync<'static, UiError>>, list_builder: &mut ListBuilder<'_, SessionId, Background<Text>>,
name: &str, name: &str,
sessions: &[HalfSession], sessions: &[HalfSession],
own_session: &SessionView, own_session: &SessionView,
@ -107,13 +107,13 @@ fn render_section(
let heading_style = Style::new().bold(); let heading_style = Style::new().bold();
if !list_builder.is_empty() { if !list_builder.is_empty() {
list_builder.add_unsel(Empty::new().boxed_async()); list_builder.add_unsel(Text::new("").background());
} }
let row = Styled::new_plain(" ") let row = Styled::new_plain(" ")
.then(name, heading_style) .then(name, heading_style)
.then_plain(format!(" ({})", sessions.len())); .then_plain(format!(" ({})", sessions.len()));
list_builder.add_unsel(Text::new(row).boxed_async()); list_builder.add_unsel(Text::new(row).background());
for session in sessions { for session in sessions {
render_row(list_builder, session, own_session, focused); render_row(list_builder, session, own_session, focused);
@ -121,7 +121,7 @@ fn render_section(
} }
fn render_row( fn render_row(
list_builder: &mut ListBuilder<'_, SessionId, BoxedAsync<'static, UiError>>, list_builder: &mut ListBuilder<'_, SessionId, Background<Text>>,
session: &HalfSession, session: &HalfSession,
own_session: &SessionView, own_session: &SessionView,
focused: bool, focused: bool,
@ -163,15 +163,12 @@ fn render_row(
let text = Styled::new_plain(owner) let text = Styled::new_plain(owner)
.then(name, style_inv) .then(name, style_inv)
.then(perms, perms_style_inv); .then(perms, perms_style_inv);
Text::new(text) Text::new(text).background().with_style(style_inv)
.background()
.with_style(style_inv)
.boxed_async()
} else { } else {
let text = Styled::new_plain(owner) let text = Styled::new_plain(owner)
.then(&name, style) .then(&name, style)
.then_plain(perms); .then_plain(perms);
Text::new(text).boxed_async() Text::new(text).background()
} }
}); });
} }

View file

@ -1,6 +1,6 @@
use crossterm::style::Stylize; use crossterm::style::Stylize;
use toss::widgets::{BoxedAsync, Text}; use toss::widgets::Text;
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, Widget};
use crate::ui::widgets::Popup; use crate::ui::widgets::Popup;
use crate::ui::UiError; use crate::ui::UiError;
@ -10,19 +10,18 @@ pub enum RoomPopup {
} }
impl RoomPopup { impl RoomPopup {
fn server_error_widget(description: &str, reason: &str) -> BoxedAsync<'static, UiError> { fn server_error_widget(description: &str, reason: &str) -> impl Widget<UiError> {
let border_style = Style::new().red().bold(); let border_style = Style::new().red().bold();
let text = Styled::new_plain(description) let text = Styled::new_plain(description)
.then_plain("\n\n") .then_plain("\n\n")
.then("Reason:", Style::new().bold()) .then("Reason:", Style::new().bold())
.then_plain(" ") .then_plain(" ")
.then_plain(reason); .then_plain(reason);
Popup::new(Text::new(text), ("Error", border_style))
.with_border_style(border_style) Popup::new(Text::new(text), ("Error", border_style)).with_border_style(border_style)
.boxed_async()
} }
pub fn widget(&self) -> BoxedAsync<'static, UiError> { pub fn widget(&self) -> impl Widget<UiError> {
match self { match self {
Self::Error { Self::Error {
description, description,

View file

@ -9,7 +9,7 @@ use parking_lot::FairMutex;
use tokio::sync::oneshot::error::TryRecvError; use tokio::sync::oneshot::error::TryRecvError;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use toss::widgets::{BoxedAsync, EditorState, Join2, Layer, Text}; use toss::widgets::{BoxedAsync, EditorState, Join2, Layer, Text};
use toss::{AsyncWidget, Style, Styled, Terminal, WidgetExt}; use toss::{Style, Styled, Terminal, Widget, WidgetExt};
use crate::config; use crate::config;
use crate::euph; use crate::euph;
@ -223,16 +223,20 @@ impl EuphRoom {
match &mut self.state { match &mut self.state {
State::Normal => {} State::Normal => {}
State::Auth(editor) => layers.push(auth::widget(editor)), State::Auth(editor) => layers.push(auth::widget(editor).desync().boxed_async()),
State::Nick(editor) => layers.push(nick::widget(editor)), State::Nick(editor) => layers.push(nick::widget(editor).desync().boxed_async()),
State::Account(account) => layers.push(account.widget()), State::Account(account) => layers.push(account.widget().desync().boxed_async()),
State::Links(links) => layers.push(links.widget()), State::Links(links) => layers.push(links.widget().desync().boxed_async()),
State::InspectMessage(message) => layers.push(inspect::message_widget(message)), State::InspectMessage(message) => {
State::InspectSession(session) => layers.push(inspect::session_widget(session)), layers.push(inspect::message_widget(message).desync().boxed_async())
}
State::InspectSession(session) => {
layers.push(inspect::session_widget(session).desync().boxed_async())
}
} }
for popup in &self.popups { for popup in &self.popups {
layers.push(popup.widget()); layers.push(popup.widget().desync().boxed_async());
} }
Layer::new(layers).boxed_async() Layer::new(layers).boxed_async()
@ -240,12 +244,12 @@ impl EuphRoom {
fn widget_without_nick_list( fn widget_without_nick_list(
chat: &mut EuphChatState, chat: &mut EuphChatState,
status_widget: impl AsyncWidget<UiError> + Send + Sync + 'static, status_widget: impl Widget<UiError> + Send + Sync + 'static,
) -> BoxedAsync<'_, UiError> { ) -> BoxedAsync<'_, UiError> {
let chat_widget = chat.widget(String::new(), true); let chat_widget = chat.widget(String::new(), true);
Join2::vertical( Join2::vertical(
status_widget.segment().with_fixed(true), status_widget.desync().segment().with_fixed(true),
chat_widget.segment(), chat_widget.segment(),
) )
.boxed_async() .boxed_async()
@ -253,7 +257,7 @@ impl EuphRoom {
fn widget_with_nick_list<'a>( fn widget_with_nick_list<'a>(
chat: &'a mut EuphChatState, chat: &'a mut EuphChatState,
status_widget: impl AsyncWidget<UiError> + Send + Sync + 'static, status_widget: impl Widget<UiError> + Send + Sync + 'static,
nick_list: &'a mut ListState<SessionId>, nick_list: &'a mut ListState<SessionId>,
joined: &Joined, joined: &Joined,
focus: Focus, focus: Focus,
@ -261,13 +265,14 @@ impl EuphRoom {
let nick_list_widget = nick_list::widget(nick_list, joined, focus == Focus::NickList) let nick_list_widget = nick_list::widget(nick_list, joined, focus == Focus::NickList)
.padding() .padding()
.with_right(1) .with_right(1)
.border(); .border()
.desync();
let chat_widget = chat.widget(joined.session.name.clone(), focus == Focus::Chat); let chat_widget = chat.widget(joined.session.name.clone(), focus == Focus::Chat);
Join2::horizontal( Join2::horizontal(
Join2::vertical( Join2::vertical(
status_widget.segment().with_fixed(true), status_widget.desync().segment().with_fixed(true),
chat_widget.segment(), chat_widget.segment(),
) )
.segment(), .segment(),
@ -276,7 +281,7 @@ impl EuphRoom {
.boxed_async() .boxed_async()
} }
async fn status_widget(&self, state: Option<&euph::State>) -> BoxedAsync<'static, UiError> { async fn status_widget(&self, state: Option<&euph::State>) -> impl Widget<UiError> {
let room_style = Style::new().bold().blue(); let room_style = Style::new().bold().blue();
let mut info = Styled::new(format!("&{}", self.name()), room_style); let mut info = Styled::new(format!("&{}", self.name()), room_style);
@ -309,11 +314,7 @@ impl EuphRoom {
.then_plain(")"); .then_plain(")");
} }
Text::new(info) Text::new(info).padding().with_horizontal(1).border()
.padding()
.with_horizontal(1)
.border()
.boxed_async()
} }
async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList) { async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList) {

View file

@ -2,8 +2,8 @@ use std::convert::Infallible;
use crossterm::event::{Event, KeyCode, KeyModifiers}; use crossterm::event::{Event, KeyCode, KeyModifiers};
use crossterm::style::Stylize; use crossterm::style::Stylize;
use toss::widgets::{BoxedAsync, Empty, Join2, Text}; use toss::widgets::{Empty, Join2, Text};
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, Widget, WidgetExt};
use super::widgets::{ListBuilder, ListState}; use super::widgets::{ListBuilder, ListState};
use super::UiError; use super::UiError;
@ -96,11 +96,11 @@ impl KeyBindingsList {
Style::new().cyan() Style::new().cyan()
} }
fn row_widget(row: Row) -> BoxedAsync<'static, UiError> { fn row_widget(row: Row) -> impl Widget<UiError> {
match row { match row {
Row::Empty => Empty::new().boxed_async(), Row::Empty => Text::new("").first3(),
Row::Heading(name) => Text::new((name, Style::new().bold())).boxed_async(), Row::Heading(name) => Text::new((name, Style::new().bold())).first3(),
Row::Binding(binding, description) => Join2::horizontal( Row::Binding(binding, description) => Join2::horizontal(
Text::new((binding, Self::binding_style())) Text::new((binding, Self::binding_style()))
@ -111,17 +111,17 @@ impl KeyBindingsList {
.segment(), .segment(),
Text::new(description).segment(), Text::new(description).segment(),
) )
.boxed_async(), .second3(),
Row::BindingContd(description) => Join2::horizontal( Row::BindingContd(description) => Join2::horizontal(
Empty::new().with_width(Self::BINDING_WIDTH).segment(), Empty::new().with_width(Self::BINDING_WIDTH).segment(),
Text::new(description).segment(), Text::new(description).segment(),
) )
.boxed_async(), .third3(),
} }
} }
pub fn widget(self, list_state: &mut ListState<Infallible>) -> BoxedAsync<'_, UiError> { pub fn widget(self, list_state: &mut ListState<Infallible>) -> impl Widget<UiError> + '_ {
let binding_style = Self::binding_style(); let binding_style = Self::binding_style();
let hint_text = Styled::new("jk/↓↑", binding_style) let hint_text = Styled::new("jk/↓↑", binding_style)
@ -150,7 +150,6 @@ impl KeyBindingsList {
.background() .background()
.float() .float()
.with_center() .with_center()
.boxed_async()
} }
pub fn empty(&mut self) { pub fn empty(&mut self) {

View file

@ -9,7 +9,7 @@ use euphoxide::conn::{self, Joined};
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use toss::widgets::{BoxedAsync, EditorState, Empty, Join2, Text}; use toss::widgets::{BoxedAsync, EditorState, Empty, Join2, Text};
use toss::{Style, Styled, Terminal, WidgetExt}; use toss::{Style, Styled, Terminal, Widget, WidgetExt};
use crate::config::{Config, RoomsSortOrder}; use crate::config::{Config, RoomsSortOrder};
use crate::euph; use crate::euph;
@ -171,9 +171,10 @@ impl Rooms {
} }
match &mut self.state { match &mut self.state {
State::ShowList => { State::ShowList => Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order)
Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order).await .await
} .desync()
.boxed_async(),
State::ShowRoom(name) => { State::ShowRoom(name) => {
self.euph_rooms self.euph_rooms
@ -187,6 +188,7 @@ impl Rooms {
Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order) Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order)
.await .await
.below(Self::new_room_widget(editor)) .below(Self::new_room_widget(editor))
.desync()
.boxed_async() .boxed_async()
} }
@ -194,12 +196,13 @@ impl Rooms {
Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order) Self::rooms_widget(&mut self.list, &self.euph_rooms, self.order)
.await .await
.below(Self::delete_room_widget(name, editor)) .below(Self::delete_room_widget(name, editor))
.desync()
.boxed_async() .boxed_async()
} }
} }
} }
fn new_room_widget(editor: &mut EditorState) -> BoxedAsync<'_, UiError> { fn new_room_widget(editor: &mut EditorState) -> impl Widget<UiError> + '_ {
let room_style = Style::new().bold().blue(); let room_style = Style::new().bold().blue();
let inner = Join2::horizontal( let inner = Join2::horizontal(
@ -210,10 +213,13 @@ impl Rooms {
.segment(), .segment(),
); );
Popup::new(inner, "Connect to").boxed_async() Popup::new(inner, "Connect to")
} }
fn delete_room_widget<'a>(name: &str, editor: &'a mut EditorState) -> BoxedAsync<'a, UiError> { fn delete_room_widget<'a>(
name: &str,
editor: &'a mut EditorState,
) -> impl Widget<UiError> + 'a {
let warn_style = Style::new().bold().red(); let warn_style = Style::new().bold().red();
let room_style = Style::new().bold().blue(); let room_style = Style::new().bold().blue();
let text = Styled::new_plain("Are you sure you want to delete ") let text = Styled::new_plain("Are you sure you want to delete ")
@ -249,9 +255,7 @@ impl Rooms {
.segment(), .segment(),
); );
Popup::new(inner, "Delete room") Popup::new(inner, "Delete room").with_border_style(warn_style)
.with_border_style(warn_style)
.boxed_async()
} }
fn format_pbln(joined: &Joined) -> String { fn format_pbln(joined: &Joined) -> String {
@ -387,7 +391,7 @@ impl Rooms {
list: &'a mut ListState<String>, list: &'a mut ListState<String>,
euph_rooms: &HashMap<String, EuphRoom>, euph_rooms: &HashMap<String, EuphRoom>,
order: Order, order: Order,
) -> BoxedAsync<'a, UiError> { ) -> impl Widget<UiError> + 'a {
let heading_style = Style::new().bold(); let heading_style = Style::new().bold();
let heading_text = let heading_text =
Styled::new("Rooms", heading_style).then_plain(format!(" ({})", euph_rooms.len())); Styled::new("Rooms", heading_style).then_plain(format!(" ({})", euph_rooms.len()));
@ -399,7 +403,6 @@ impl Rooms {
Text::new(heading_text).segment().with_fixed(true), Text::new(heading_text).segment().with_fixed(true),
list_builder.build(list).segment(), list_builder.build(list).segment(),
) )
.boxed_async()
} }
fn room_char(c: char) -> bool { fn room_char(c: char) -> bool {

View file

@ -1,7 +1,6 @@
use std::vec; use std::vec;
use async_trait::async_trait; use toss::{Frame, Pos, Size, Widget, WidthDb};
use toss::{AsyncWidget, Frame, Pos, Size, WidthDb};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Cursor<Id> { struct Cursor<Id> {
@ -298,13 +297,12 @@ pub struct List<'a, Id, W> {
rows: Vec<W>, rows: Vec<W>,
} }
#[async_trait] impl<Id, E, W> Widget<E> for List<'_, Id, W>
impl<Id, E, W> AsyncWidget<E> for List<'_, Id, W>
where where
Id: Clone + Eq + Send + Sync, Id: Clone + Eq,
W: AsyncWidget<E> + Send + Sync, W: Widget<E>,
{ {
async fn size( fn size(
&self, &self,
widthdb: &mut WidthDb, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
@ -312,14 +310,14 @@ where
) -> Result<Size, E> { ) -> Result<Size, E> {
let mut width = 0; let mut width = 0;
for row in &self.rows { for row in &self.rows {
let size = row.size(widthdb, max_width, Some(1)).await?; let size = row.size(widthdb, max_width, Some(1))?;
width = width.max(size.width); width = width.max(size.width);
} }
let height = self.rows.len().try_into().unwrap_or(u16::MAX); let height = self.rows.len().try_into().unwrap_or(u16::MAX);
Ok(Size::new(width, height)) Ok(Size::new(width, height))
} }
async fn draw(self, frame: &mut Frame) -> Result<(), E> { fn draw(self, frame: &mut Frame) -> Result<(), E> {
let size = frame.size(); let size = frame.size();
self.state.last_height = size.height; self.state.last_height = size.height;
@ -332,7 +330,7 @@ where
.enumerate() .enumerate()
{ {
frame.push(Pos::new(0, y as i32), Size::new(size.width, 1)); frame.push(Pos::new(0, y as i32), Size::new(size.width, 1));
row.draw(frame).await?; row.draw(frame)?;
frame.pop(); frame.pop();
} }

View file

@ -1,11 +1,10 @@
use async_trait::async_trait; use toss::widgets::{Background, Border, Desync, Float, Layer2, Padding, Text};
use toss::widgets::{Background, Border, Float, Layer2, Padding, Text}; use toss::{Frame, Size, Style, Styled, Widget, WidgetExt, WidthDb};
use toss::{AsyncWidget, Frame, Size, Style, Styled, WidgetExt, WidthDb};
type Body<I> = Background<Border<Padding<I>>>; type Body<I> = Background<Border<Padding<I>>>;
type Title = Float<Padding<Background<Padding<Text>>>>; type Title = Float<Padding<Background<Padding<Text>>>>;
pub struct Popup<I>(Float<Layer2<Body<I>, Title>>); pub struct Popup<I>(Float<Layer2<Body<I>, Desync<Title>>>);
impl<I> Popup<I> { impl<I> Popup<I> {
pub fn new<S: Into<Styled>>(inner: I, title: S) -> Self { pub fn new<S: Into<Styled>>(inner: I, title: S) -> Self {
@ -19,7 +18,8 @@ impl<I> Popup<I> {
.with_horizontal(2) .with_horizontal(2)
.float() .float()
.with_top() .with_top()
.with_left(); .with_left()
.desync();
let body = inner.padding().with_horizontal(1).border().background(); let body = inner.padding().with_horizontal(1).border().background();
@ -33,22 +33,20 @@ impl<I> Popup<I> {
} }
} }
#[async_trait] impl<E, I> Widget<E> for Popup<I>
impl<E, I> AsyncWidget<E> for Popup<I>
where where
E: Send, I: Widget<E>,
I: AsyncWidget<E> + Send + Sync,
{ {
async fn size( fn size(
&self, &self,
widthdb: &mut WidthDb, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
max_height: Option<u16>, max_height: Option<u16>,
) -> Result<Size, E> { ) -> Result<Size, E> {
self.0.size(widthdb, max_width, max_height).await self.0.size(widthdb, max_width, max_height)
} }
async fn draw(self, frame: &mut Frame) -> Result<(), E> { fn draw(self, frame: &mut Frame) -> Result<(), E> {
self.0.draw(frame).await self.0.draw(frame)
} }
} }