From 970bc07ed9154247555613a6d89f991a6567373b Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 2 Jan 2024 13:51:51 +0100 Subject: [PATCH] Support choosing domain in room connection popup --- cove/src/ui/rooms.rs | 55 +++++----------- cove/src/ui/rooms/connect.rs | 120 +++++++++++++++++++++++++++++++++++ cove/src/ui/util.rs | 5 ++ 3 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 cove/src/ui/rooms/connect.rs diff --git a/cove/src/ui/rooms.rs b/cove/src/ui/rooms.rs index 57b5aca..3521318 100644 --- a/cove/src/ui/rooms.rs +++ b/cove/src/ui/rooms.rs @@ -1,3 +1,5 @@ +mod connect; + use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::iter; @@ -17,6 +19,8 @@ use crate::euph; use crate::macros::logging_unwrap; use crate::vault::{EuphVault, RoomIdentifier, Vault}; +use self::connect::{ConnectResult, ConnectState}; + use super::euph::room::EuphRoom; use super::widgets::{ListBuilder, ListState, Popup}; use super::{key_bindings, util, UiError, UiEvent}; @@ -24,7 +28,7 @@ use super::{key_bindings, util, UiError, UiEvent}; enum State { ShowList, ShowRoom(RoomIdentifier), - Connect(EditorState), + Connect(ConnectState), Delete(RoomIdentifier, EditorState), } @@ -242,10 +246,10 @@ impl Rooms { .await } - State::Connect(editor) => { + State::Connect(connect) => { Self::rooms_widget(self.config, &mut self.list, self.order, &self.euph_rooms) .await - .below(Self::new_room_widget(editor)) + .below(connect.widget()) .desync() .boxed_async() } @@ -261,20 +265,6 @@ impl Rooms { } } - fn new_room_widget(editor: &mut EditorState) -> impl Widget + '_ { - let room_style = Style::new().bold().blue(); - - let inner = Join2::horizontal( - Text::new(("&", room_style)).segment().with_fixed(true), - editor - .widget() - .with_highlight(|s| Styled::new(s, room_style)) - .segment(), - ); - - Popup::new(inner, "Connect to") - } - fn delete_room_widget<'a>( id: &RoomIdentifier, editor: &'a mut EditorState, @@ -472,10 +462,6 @@ impl Rooms { ) } - fn room_char(c: char) -> bool { - c.is_ascii_alphanumeric() || c == '_' - } - async fn handle_showlist_input_event( &mut self, event: &mut InputEvent<'_>, @@ -534,7 +520,7 @@ impl Rooms { return true; } if event.matches(&keys.rooms.action.new) { - self.state = State::Connect(EditorState::new()); + self.state = State::Connect(ConnectState::new()); return true; } if event.matches(&keys.rooms.action.delete) { @@ -574,28 +560,21 @@ impl Rooms { } } } - State::Connect(editor) => { - if event.matches(&keys.general.abort) { + State::Connect(connect) => match connect.handle_input_event(event, keys) { + ConnectResult::Close => { self.state = State::ShowList; return true; } - if event.matches(&keys.general.confirm) { - let name = editor.text().to_string(); - if !name.is_empty() { - let room = RoomIdentifier { - // TODO Remove hardcoded domain - domain: "euphoria.leet.nu".to_string(), - name, - }; - self.connect_to_room(room.clone()).await; - self.state = State::ShowRoom(room); - } + ConnectResult::Connect(room) => { + self.connect_to_room(room.clone()).await; + self.state = State::ShowRoom(room); return true; } - if util::handle_editor_input_event(editor, event, keys, Self::room_char) { + ConnectResult::Handled => { return true; } - } + ConnectResult::Unhandled => {} + }, State::Delete(id, editor) => { if event.matches(&keys.general.abort) { self.state = State::ShowList; @@ -607,7 +586,7 @@ impl Rooms { self.state = State::ShowList; return true; } - if util::handle_editor_input_event(editor, event, keys, Self::room_char) { + if util::handle_editor_input_event(editor, event, keys, util::is_room_char) { return true; } } diff --git a/cove/src/ui/rooms/connect.rs b/cove/src/ui/rooms/connect.rs new file mode 100644 index 0000000..fed584b --- /dev/null +++ b/cove/src/ui/rooms/connect.rs @@ -0,0 +1,120 @@ +use cove_config::Keys; +use cove_input::InputEvent; +use crossterm::style::Stylize; +use toss::widgets::{EditorState, Empty, Join2, Join3, Text}; +use toss::{Style, Styled, Widget, WidgetExt}; + +use crate::ui::widgets::Popup; +use crate::ui::{util, UiError}; +use crate::vault::RoomIdentifier; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Focus { + Name, + Domain, +} + +impl Focus { + fn advance(self) -> Self { + match self { + Self::Name => Self::Domain, + Self::Domain => Self::Name, + } + } +} + +pub struct ConnectState { + focus: Focus, + name: EditorState, + domain: EditorState, +} + +pub enum ConnectResult { + Close, + Connect(RoomIdentifier), + Handled, + Unhandled, +} + +impl ConnectState { + pub fn new() -> Self { + Self { + focus: Focus::Name, + name: EditorState::new(), + domain: EditorState::with_initial_text("euphoria.leet.nu".to_string()), + } + } + + pub fn handle_input_event(&mut self, event: &mut InputEvent<'_>, keys: &Keys) -> ConnectResult { + if event.matches(&keys.general.abort) { + return ConnectResult::Close; + } + + if event.matches(&keys.general.focus) { + self.focus = self.focus.advance(); + return ConnectResult::Handled; + } + + if event.matches(&keys.general.confirm) { + let id = RoomIdentifier { + domain: self.domain.text().to_string(), + name: self.name.text().to_string(), + }; + if !id.domain.is_empty() && !id.name.is_empty() { + return ConnectResult::Connect(id); + } + } + + let handled = match self.focus { + Focus::Name => { + util::handle_editor_input_event(&mut self.name, event, keys, util::is_room_char) + } + Focus::Domain => { + util::handle_editor_input_event(&mut self.domain, event, keys, util::is_room_char) + } + }; + + if handled { + return ConnectResult::Handled; + } + + ConnectResult::Unhandled + } + + pub fn widget(&mut self) -> impl Widget + '_ { + let room_style = Style::new().bold().blue(); + let domain_style = Style::new().grey(); + + let name = Join2::horizontal( + Text::new(Styled::new_plain("Room: ").then("&", room_style)) + .with_wrap(false) + .segment() + .with_fixed(true), + self.name + .widget() + .with_highlight(|s| Styled::new(s, room_style)) + .with_focus(self.focus == Focus::Name) + .segment(), + ); + + let domain = Join3::horizontal( + Text::new("Domain:") + .with_wrap(false) + .segment() + .with_fixed(true), + Empty::new().with_width(1).segment().with_fixed(true), + self.domain + .widget() + .with_highlight(|s| Styled::new(s, domain_style)) + .with_focus(self.focus == Focus::Domain) + .segment(), + ); + + let inner = Join2::vertical( + name.segment().with_fixed(true), + domain.segment().with_fixed(true), + ); + + Popup::new(inner, "Connect to") + } +} diff --git a/cove/src/ui/util.rs b/cove/src/ui/util.rs index fa434fe..b358588 100644 --- a/cove/src/ui/util.rs +++ b/cove/src/ui/util.rs @@ -5,6 +5,11 @@ use toss::widgets::EditorState; use super::widgets::ListState; +/// Test if a character is allowed to be typed in a room name. +pub fn is_room_char(c: char) -> bool { + c.is_ascii_alphanumeric() || c == '_' +} + ////////// // List // //////////