refactor(tui): one page window and one filter editor for every list

Four panes had grown the same dance around ratatui's table: clamp the
selection, take the visible page, hand the table a page-local state,
keep the page start. It is now page_window and render_page in a paging
module beside page_of, and the two popups page by the same count they
draw, tab bar or header included.

Five typed filters had grown the same key handling too. edit_filter now
takes a key for all of them, which also stops a control or alt chord
from typing its letter into a filter, stops Enter, which keeps a filter,
from resetting the view it is over, and lets the results and room-list
filters page like the others. Their titles and the typing shortcuts
come from one place as well.
This commit is contained in:
Michel de Graaf
2026-09-08 11:24:59 +02:00
parent 607532cc44
commit 0cacc8cc10
13 changed files with 293 additions and 299 deletions
+23 -39
View File
@@ -1,5 +1,5 @@
use super::MainTui;
use super::input::{jumped, list_jump, scroll_log};
use super::input::{FilterEdit, edit_filter, jumped, list_jump, scroll_log};
use crate::models::{ChatFilter, RoomsView};
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
@@ -47,7 +47,7 @@ impl MainTui {
fn handle_rooms_list_input(&mut self, key: KeyEvent) {
let len = self.state.rooms.filtered_rooms().len();
if let Some(jump) = list_jump(key, self.popup_page()) {
if let Some(jump) = list_jump(key, self.popup_page(1)) {
self.state.rooms.list_selected =
jumped(self.state.rooms.list_selected, len, jump);
return;
@@ -88,25 +88,18 @@ impl MainTui {
}
fn handle_room_filter_input(&mut self, key: KeyEvent) {
match key.code {
KeyCode::Esc => {
self.state.rooms.list_is_filtering = false;
self.state.rooms.list_filter.clear();
let Some(edit) = edit_filter(&mut self.state.rooms.list_filter, key)
else {
return self.handle_rooms_list_input(key);
};
match edit {
FilterEdit::Changed | FilterEdit::Cleared => {
self.state.rooms.list_selected = 0;
}
KeyCode::Enter => {
self.state.rooms.list_is_filtering = false;
self.join_selected_room();
}
KeyCode::Char(c) => {
self.state.rooms.list_filter.push(c);
self.state.rooms.list_selected = 0;
}
KeyCode::Backspace => {
self.state.rooms.list_filter.pop();
self.state.rooms.list_selected = 0;
}
_ => {}
FilterEdit::Kept => self.join_selected_room(),
}
if edit != FilterEdit::Changed {
self.state.rooms.list_is_filtering = false;
}
}
@@ -163,33 +156,24 @@ impl MainTui {
fn handle_chat_filter_input(&mut self, key: KeyEvent) -> bool {
let rooms = &mut self.state.rooms;
let over_log = rooms.filtering == Some(ChatFilter::Log);
let current = if over_log {
let mut filter = if over_log {
rooms.log_filter.clone()
} else {
rooms.user_filter.clone()
};
let next = match key.code {
KeyCode::Esc => String::new(),
KeyCode::Enter => current,
KeyCode::Char(c) => {
let mut next = current;
next.push(c);
next
}
KeyCode::Backspace => {
let mut next = current;
next.pop();
next
}
_ => return false,
let Some(edit) = edit_filter(&mut filter, key) else {
return false;
};
if matches!(key.code, KeyCode::Esc | KeyCode::Enter) {
if edit != FilterEdit::Changed {
rooms.filtering = None;
}
if over_log {
rooms.set_log_filter(next);
} else {
rooms.set_user_filter(next);
// Keeping a filter leaves the view where it is.
if edit != FilterEdit::Kept {
if over_log {
rooms.set_log_filter(filter);
} else {
rooms.set_user_filter(filter);
}
}
true
}