aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar lancebord 2026-03-09 00:43:26 -0400
committerGravatar lancebord 2026-03-09 00:43:26 -0400
commit744c5cc2960cbc29802739bbb5851cfa429316b6 (patch)
tree7deec9c01a85ac674f5712bf5428fa437a654eac
parentfixing text wrap miscalculation on long words (diff)
add scroll and scroll bar indicator to members list
-rw-r--r--src/main.rs2
-rw-r--r--src/tui/app.rs27
-rw-r--r--src/tui/ui.rs24
3 files changed, 44 insertions, 9 deletions
diff --git a/src/main.rs b/src/main.rs
index b87ee8d..b81eff8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -105,6 +105,8 @@ async fn run(
(KeyModifiers::NONE | KeyModifiers::SHIFT, KeyCode::Char(c)) => {
app.input_insert(c);
}
+ (KeyModifiers::SHIFT, KeyCode::Up) => app.members_scroll_up(),
+ (KeyModifiers::SHIFT, KeyCode::Down) => app.members_scroll_down(),
(_, KeyCode::Backspace) => app.input_backspace(),
(_, KeyCode::Left) => app.cursor_left(),
(_, KeyCode::Right) => app.cursor_right(),
diff --git a/src/tui/app.rs b/src/tui/app.rs
index 879d762..52fc85a 100644
--- a/src/tui/app.rs
+++ b/src/tui/app.rs
@@ -1,3 +1,5 @@
+use ratatui::widgets::{ListState, ScrollbarState};
+
/// A single chat message in the log
#[derive(Clone)]
pub struct ChatLine {
@@ -15,7 +17,9 @@ pub struct AppState {
pub members: Vec<String>,
pub input: String,
pub cursor: usize,
- pub scroll_offset: usize,
+ pub chat_scroll: usize,
+ pub members_scroll: ScrollbarState,
+ pub members_list_state: ListState,
pub status: String,
pub connected: bool,
}
@@ -29,7 +33,9 @@ impl AppState {
members: Vec::new(),
input: String::new(),
cursor: 0,
- scroll_offset: 0,
+ chat_scroll: 0,
+ members_scroll: ScrollbarState::new(0),
+ members_list_state: ListState::default(),
status: "Set nick with /nick to connect.".into(),
connected: false,
}
@@ -88,11 +94,24 @@ impl AppState {
}
pub fn scroll_up(&mut self) {
- self.scroll_offset += 1;
+ self.chat_scroll = self.chat_scroll.saturating_add(1);
}
pub fn scroll_down(&mut self) {
- self.scroll_offset = self.scroll_offset.saturating_sub(1);
+ self.chat_scroll = self.chat_scroll.saturating_sub(1);
+ }
+
+ pub fn members_scroll_up(&mut self) {
+ let pos = self.members_scroll.get_position().saturating_sub(5);
+ self.members_scroll = self.members_scroll.position(pos);
+ *self.members_list_state.offset_mut() = pos;
+ }
+
+ pub fn members_scroll_down(&mut self) {
+ let max = self.members.len().saturating_sub(1);
+ let pos = (self.members_scroll.get_position().saturating_add(5)).min(max);
+ self.members_scroll = self.members_scroll.position(pos);
+ *self.members_list_state.offset_mut() = pos;
}
pub fn take_input(&mut self) -> String {
diff --git a/src/tui/ui.rs b/src/tui/ui.rs
index b026c34..05b3c9f 100644
--- a/src/tui/ui.rs
+++ b/src/tui/ui.rs
@@ -4,7 +4,10 @@ use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span, Text},
- widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Wrap},
+ widgets::{
+ Block, BorderType, Borders, List, ListItem, Paragraph, Scrollbar, ScrollbarOrientation,
+ Wrap,
+ },
};
use unicode_width::UnicodeWidthStr;
@@ -113,8 +116,8 @@ fn draw_chat_log(f: &mut Frame, area: Rect, state: &mut AppState) {
let max_offset = base_scroll as usize;
// Clamp the offset and write it back so app.rs stays in sync
- state.scroll_offset = state.scroll_offset.clamp(0, max_offset);
- let final_scroll = (base_scroll as i32 - state.scroll_offset as i32) as u16;
+ state.chat_scroll = state.chat_scroll.clamp(0, max_offset);
+ let final_scroll = (base_scroll as i32 - state.chat_scroll as i32) as u16;
f.render_widget(
Paragraph::new(Text::from(padded_lines))
@@ -246,7 +249,7 @@ fn draw_input(f: &mut Frame, area: Rect, state: &AppState) {
);
}
-fn draw_members_panel(f: &mut Frame, area: Rect, state: &AppState) {
+fn draw_members_panel(f: &mut Frame, area: Rect, state: &mut AppState) {
let items: Vec<ListItem> = state
.members
.iter()
@@ -281,7 +284,18 @@ fn draw_members_panel(f: &mut Frame, area: Rect, state: &AppState) {
let title = format!(" users ({}) ", state.members.len());
let block = panel_block(&title);
- f.render_widget(List::new(items).block(block), area);
+ state.members_scroll = state.members_scroll.content_length(state.members.len());
+
+ f.render_stateful_widget(
+ List::new(items).block(block),
+ area,
+ &mut state.members_list_state,
+ );
+ f.render_stateful_widget(
+ Scrollbar::new(ScrollbarOrientation::VerticalRight),
+ area,
+ &mut state.members_scroll,
+ );
}
fn draw_statusbar(f: &mut Frame, area: Rect, state: &AppState) {