aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs2
-rw-r--r--src/tui/app.rs27
-rw-r--r--src/tui/ui.rs42
3 files changed, 57 insertions, 14 deletions
diff --git a/src/main.rs b/src/main.rs
index d955dc2..f66ce13 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -147,6 +147,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 0fc1ec9..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))
@@ -129,6 +132,7 @@ fn count_wrapped_lines(lines: &[Line], width: usize) -> usize {
if width == 0 {
return lines.len();
}
+
lines
.iter()
.map(|line| {
@@ -146,13 +150,20 @@ fn count_wrapped_lines(lines: &[Line], width: usize) -> usize {
let mut current_width = 0;
for word in full_text.split_inclusive(' ') {
- let word_width = UnicodeWidthStr::width(word);
- if current_width > 0 && current_width + word_width > width {
- row_count += 1;
- current_width = word_width;
- } else {
+ let mut word_width = UnicodeWidthStr::width(word);
+ if current_width + word_width <= width {
current_width += word_width;
+ continue;
}
+
+ row_count += 1;
+
+ if word_width >= width {
+ row_count += word_width / width;
+ word_width %= width;
+ }
+
+ current_width = word_width;
}
row_count
})
@@ -238,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()
@@ -273,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) {