Switch focus using tab

Also refactored some key event handling code in the process.
This commit is contained in:
Joscha 2022-09-25 21:20:43 +02:00
parent 8703a62887
commit 9c9d9a51bb

View file

@ -322,74 +322,20 @@ impl EuphRoom {
Text::new(info).into() Text::new(info).into()
} }
fn list_inspect_initiating_key_bindings(&self, bindings: &mut KeyBindingsList) { async fn list_chat_key_bindings(&self, bindings: &mut KeyBindingsList, status: &RoomStatus) {
bindings.binding("i", "inspect message"); let can_compose = matches!(status, RoomStatus::Connected(Status::Joined(_)));
bindings.binding("I", "show message links");
}
async fn handle_inspect_initiating_input_event(&mut self, event: &InputEvent) -> bool {
match event {
key!('i') => {
if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault.full_msg(id).await {
self.state = State::InspectMessage(msg);
}
}
true
}
key!('I') => {
if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault.msg(id).await {
self.state = State::Links(LinksState::new(&msg.content));
}
}
true
}
_ => false,
}
}
pub async fn list_normal_key_bindings(&self, bindings: &mut KeyBindingsList) {
bindings.binding("esc", "leave room");
let can_compose = if let Some(room) = &self.room {
match room.status().await.ok().flatten() {
Some(Status::Joining(Joining {
bounce: Some(_), ..
})) => {
bindings.binding("a", "authenticate");
false
}
Some(Status::Joined(_)) => {
bindings.binding("n", "change nick");
bindings.binding("m", "download more messages");
bindings.binding("A", "show account ui");
true
}
_ => false,
}
} else {
false
};
self.list_inspect_initiating_key_bindings(bindings);
bindings.empty();
self.chat.list_key_bindings(bindings, can_compose).await; self.chat.list_key_bindings(bindings, can_compose).await;
} }
async fn handle_normal_input_event( async fn handle_chat_input_event(
&mut self, &mut self,
terminal: &mut Terminal, terminal: &mut Terminal,
crossterm_lock: &Arc<FairMutex<()>>, crossterm_lock: &Arc<FairMutex<()>>,
event: &InputEvent, event: &InputEvent,
status: &RoomStatus,
) -> bool { ) -> bool {
if let Some(room) = &self.room { let can_compose = matches!(status, RoomStatus::Connected(Status::Joined(_)));
let status = room.status().await;
let can_compose = matches!(status, Ok(Some(Status::Joined(_))));
// We need to handle chat input first, otherwise the other
// key bindings will shadow characters in the editor.
match self match self
.chat .chat
.handle_input_event(terminal, crossterm_lock, event, can_compose) .handle_input_event(terminal, crossterm_lock, event, can_compose)
@ -398,12 +344,14 @@ impl EuphRoom {
Reaction::NotHandled => {} Reaction::NotHandled => {}
Reaction::Handled => return true, Reaction::Handled => return true,
Reaction::Composed { parent, content } => { Reaction::Composed { parent, content } => {
if let Some(room) = &self.room {
match room.send(parent, content) { match room.send(parent, content) {
Ok(id_rx) => self.last_msg_sent = Some(id_rx), Ok(id_rx) => self.last_msg_sent = Some(id_rx),
Err(_) => self.chat.sent(None).await, Err(_) => self.chat.sent(None).await,
} }
return true; return true;
} }
}
Reaction::ComposeError(e) => { Reaction::ComposeError(e) => {
self.popups.push_front(RoomPopup::Error { self.popups.push_front(RoomPopup::Error {
description: "Failed to use external editor".to_string(), description: "Failed to use external editor".to_string(),
@ -413,52 +361,182 @@ impl EuphRoom {
} }
} }
if self.handle_inspect_initiating_input_event(event).await { false
return true;
} }
match status.ok().flatten() { fn list_room_key_bindings(&self, bindings: &mut KeyBindingsList, status: &RoomStatus) {
Some(Status::Joining(Joining { match status {
// Authenticating
RoomStatus::Connected(Status::Joining(Joining {
bounce: Some(_), .. bounce: Some(_), ..
})) if matches!(event, key!('a')) => { })) => {
self.state = State::Auth(auth::new()); bindings.binding("a", "authenticate");
true
} }
Some(Status::Joined(joined)) => match event {
// Connected
RoomStatus::Connected(Status::Joined(_)) => {
bindings.binding("n", "change nick");
bindings.binding("m", "download more messages");
bindings.binding("A", "show account ui");
}
// Otherwise
_ => {}
}
// Inspecting messages
bindings.binding("i", "inspect message");
bindings.binding("I", "show message links");
}
async fn handle_room_input_event(&mut self, event: &InputEvent, status: &RoomStatus) -> bool {
match status {
// Authenticating
RoomStatus::Connected(Status::Joining(Joining {
bounce: Some(_), ..
})) => {
if let key!('a') = event {
self.state = State::Auth(auth::new());
return true;
}
}
// Joined
RoomStatus::Connected(Status::Joined(joined)) => match event {
key!('n') | key!('N') => { key!('n') | key!('N') => {
self.state = State::Nick(nick::new(joined)); self.state = State::Nick(nick::new(joined.clone()));
true return true;
} }
key!('m') => { key!('m') => {
if let Some(room) = &self.room { if let Some(room) = &self.room {
let _ = room.log(); let _ = room.log();
} }
true return true;
} }
key!('A') => { key!('A') => {
self.state = State::Account(AccountUiState::new()); self.state = State::Account(AccountUiState::new());
true return true;
} }
_ => false, _ => {}
}, },
_ => false,
// Otherwise
_ => {}
} }
} else {
// Inspecting messages
match event {
key!('i') => {
if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault.full_msg(id).await {
self.state = State::InspectMessage(msg);
}
}
return true;
}
key!('I') => {
if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault.msg(id).await {
self.state = State::Links(LinksState::new(&msg.content));
}
}
return true;
}
_ => {}
}
false
}
async fn list_chat_focus_key_bindings(
&self,
bindings: &mut KeyBindingsList,
status: &RoomStatus,
) {
self.list_room_key_bindings(bindings, status);
bindings.empty();
self.list_chat_key_bindings(bindings, status).await;
}
async fn handle_chat_focus_input_event(
&mut self,
terminal: &mut Terminal,
crossterm_lock: &Arc<FairMutex<()>>,
event: &InputEvent,
status: &RoomStatus,
) -> bool {
// We need to handle chat input first, otherwise the other
// key bindings will shadow characters in the editor.
if self if self
.chat .handle_chat_input_event(terminal, crossterm_lock, event, status)
.handle_input_event(terminal, crossterm_lock, event, false)
.await .await
.handled()
{ {
return true; return true;
} }
if self.handle_inspect_initiating_input_event(event).await { if self.handle_room_input_event(event, status).await {
return true; return true;
} }
false false
} }
pub async fn list_normal_key_bindings(&self, bindings: &mut KeyBindingsList) {
// Handled in rooms list, not here
// TODO Move to rooms list?
bindings.binding("esc", "leave room");
let status = self.status().await;
match self.focus {
Focus::Chat => {
if let RoomStatus::Connected(Status::Joined(_)) = status {
bindings.binding("tab", "focus on nick list");
}
self.list_chat_focus_key_bindings(bindings, &status).await;
}
Focus::NickList => {
bindings.binding("tab", "focus on chat");
}
}
}
async fn handle_normal_input_event(
&mut self,
terminal: &mut Terminal,
crossterm_lock: &Arc<FairMutex<()>>,
event: &InputEvent,
) -> bool {
let status = self.status().await;
match self.focus {
Focus::Chat => {
// Needs to be handled first or the tab key may be shadowed
// during editing.
if self
.handle_chat_focus_input_event(terminal, crossterm_lock, event, &status)
.await
{
return true;
}
if let RoomStatus::Connected(Status::Joined(_)) = status {
if let key!(Tab) = event {
self.focus = Focus::NickList;
return true;
}
}
}
Focus::NickList => {
if let key!(Tab) = event {
self.focus = Focus::Chat;
return true;
}
}
}
false
} }
pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) { pub async fn list_key_bindings(&self, bindings: &mut KeyBindingsList) {