Make room list behave more like chat

Accomplished by adding the same cursor movement and scrolling key
bindings, as well as moving the cursor so it is visible when scrolling.
This commit is contained in:
Joscha 2022-08-01 01:41:43 +02:00
parent 2c3586df7c
commit f3b804347d
2 changed files with 66 additions and 15 deletions

View file

@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::iter;
use std::sync::Arc;
use crossterm::event::{KeyCode, KeyEvent};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::style::{ContentStyle, Stylize};
use parking_lot::FairMutex;
use tokio::sync::mpsc;
@ -213,10 +213,16 @@ impl Rooms {
self.state = State::ShowRoom(name);
}
}
KeyCode::Char('j') | KeyCode::Down => self.list.move_cursor_down(),
KeyCode::Char('k') | KeyCode::Up => self.list.move_cursor_up(),
KeyCode::Char('J') => self.list.scroll_down(), // TODO Replace by Ctrl+E and mouse scroll
KeyCode::Char('K') => self.list.scroll_up(), // TODO Replace by Ctrl+Y and mouse scroll
KeyCode::Char('j') | KeyCode::Down => self.list.move_cursor_down(),
KeyCode::Char('g') | KeyCode::Home => self.list.move_cursor_to_top(),
KeyCode::Char('G') | KeyCode::End => self.list.move_cursor_to_bottom(),
KeyCode::Char('y') if event.modifiers == KeyModifiers::CONTROL => {
self.list.scroll_up(1)
}
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => {
self.list.scroll_down(1)
}
KeyCode::Char('c') => {
if let Some(name) = self.list.cursor() {
self.get_or_insert_room(name).connect();

View file

@ -58,6 +58,14 @@ impl<Id: Clone> InnerListState<Id> {
.find_map(|(i, r)| r.as_ref().map(|c| Cursor::new(c.clone(), i)))
}
fn last_selectable(&self) -> Option<Cursor<Id>> {
self.rows
.iter()
.enumerate()
.rev()
.find_map(|(i, r)| r.as_ref().map(|c| Cursor::new(c.clone(), i)))
}
fn selectable_at_or_before_index(&self, i: usize) -> Option<Cursor<Id>> {
self.rows
.iter()
@ -92,7 +100,7 @@ impl<Id: Clone> InnerListState<Id> {
.find_map(|(i, r)| r.as_ref().map(|c| Cursor::new(c.clone(), i)))
}
fn make_cursor_visible(&mut self, height: usize) {
fn scroll_so_cursor_is_visible(&mut self, height: usize) {
if height == 0 {
// Cursor can't be visible because nothing is visible
return;
@ -106,6 +114,25 @@ impl<Id: Clone> InnerListState<Id> {
}
}
fn move_cursor_to_make_it_visible(&mut self, height: usize) {
if let Some(cursor) = &self.cursor {
let min_idx = self.offset;
let max_idx = self.offset.saturating_add(height).saturating_sub(1);
let new_cursor = if cursor.idx < min_idx {
self.selectable_at_or_after_index(min_idx)
} else if cursor.idx > max_idx {
self.selectable_at_or_before_index(max_idx)
} else {
return;
};
if let Some(new_cursor) = new_cursor {
self.cursor = Some(new_cursor);
}
}
}
fn clamp_scrolling(&mut self, height: usize) {
let min = 0;
let max = self.rows.len().saturating_sub(height);
@ -137,11 +164,13 @@ impl<Id: Clone + Eq> InnerListState<Id> {
self.fix_cursor();
if self.make_cursor_visible {
self.make_cursor_visible(height);
self.make_cursor_visible = false;
self.scroll_so_cursor_is_visible(height);
self.clamp_scrolling(height);
} else {
self.clamp_scrolling(height);
self.move_cursor_to_make_it_visible(height);
}
self.clamp_scrolling(height);
self.make_cursor_visible = false;
}
}
@ -156,14 +185,14 @@ impl<Id> ListState<Id> {
List::new(self.0.clone())
}
pub fn scroll_up(&mut self) {
pub fn scroll_up(&mut self, amount: usize) {
let mut guard = self.0.lock();
guard.offset = guard.offset.saturating_sub(1);
guard.offset = guard.offset.saturating_sub(amount);
}
pub fn scroll_down(&mut self) {
pub fn scroll_down(&mut self, amount: usize) {
let mut guard = self.0.lock();
guard.offset = guard.offset.saturating_add(1);
guard.offset = guard.offset.saturating_add(amount);
}
}
@ -177,9 +206,9 @@ impl<Id: Clone> ListState<Id> {
if let Some(cursor) = &guard.cursor {
if let Some(new_cursor) = guard.selectable_before_index(cursor.idx) {
guard.cursor = Some(new_cursor);
guard.make_cursor_visible = true;
}
}
guard.make_cursor_visible = true;
}
pub fn move_cursor_down(&mut self) {
@ -187,9 +216,25 @@ impl<Id: Clone> ListState<Id> {
if let Some(cursor) = &guard.cursor {
if let Some(new_cursor) = guard.selectable_after_index(cursor.idx) {
guard.cursor = Some(new_cursor);
guard.make_cursor_visible = true;
}
}
guard.make_cursor_visible = true;
}
pub fn move_cursor_to_top(&mut self) {
let mut guard = self.0.lock();
if let Some(new_cursor) = guard.first_selectable() {
guard.cursor = Some(new_cursor);
}
guard.make_cursor_visible = true;
}
pub fn move_cursor_to_bottom(&mut self) {
let mut guard = self.0.lock();
if let Some(new_cursor) = guard.last_selectable() {
guard.cursor = Some(new_cursor);
}
guard.make_cursor_visible = true;
}
}