From: Simon Tatham Date: Fri, 29 Dec 2023 18:08:43 +0000 (+0000) Subject: Scrolling upward mostly works. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=80db886568eb1ce883f84c553cf3d027e57808b1;p=mastodonochrome.git Scrolling upward mostly works. Doesn't clip at the top of the file yet, but this is a start! --- diff --git a/src/file.rs b/src/file.rs index 9360ccf..b61dc27 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,3 +1,4 @@ +use std::cmp::{min, max}; use std::collections::HashMap; use super::client::{Client, ClientError, FeedId, FeedExtend}; @@ -5,7 +6,7 @@ use super::coloured_string::ColouredString; use super::text::*; use super::tui::{ ActivityState, CursorPosition, LogicalAction, - OurKey, + OurKey, OurKey::*, }; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -60,7 +61,7 @@ struct FeedFile { contents: FeedFileContents, rendered: HashMap>, pos: FilePosition, - last_width: Option, + last_size: Option<(usize, usize)>, } impl FeedFile { @@ -82,7 +83,7 @@ impl FeedFile { contents: contents, rendered: HashMap::new(), pos: initial_pos, - last_width: None, + last_size: None, }; Ok(ff) } @@ -105,6 +106,20 @@ impl FeedFile { self.rendered.get(&index).expect("We just made sure this was present") } + fn ensure_enough_rendered(&mut self) { + let (w, h) = self.last_size.expect( + "ensure_enough_rendered before resize"); + + let (item, line) = self.refine_pos(w); + + let mut item = item; + let mut lines_rendered = line; + while item > self.contents.origin && lines_rendered + 1 < h { + item -= 1; + lines_rendered += self.ensure_item_rendered(item, w).len(); + } + } + fn refine_pos(&mut self, w: usize) -> (isize, usize) { let (newpos, item, line) = match self.pos { pos @ FilePosition::Fine(item, line) => (pos, item, line), @@ -127,28 +142,49 @@ impl FeedFile { self.pos = newpos; } + + fn move_up(&mut self, distance: usize) { + let (w, _h) = self.last_size.expect("move_up before resize"); + let (item, line) = match self.pos { + FilePosition::Fine(item, line) => (item, line), + _ => panic!("coarse position reached move_up()"), + }; + + let mut item = item; + let mut line = line; + let mut remaining = distance; + while remaining > 0 { + let this = min(line, remaining); + remaining -= this; + line -= this; + if line == 0 { + if item <= self.contents.origin { + break; + } + item -= 1; + line = self.ensure_item_rendered(item, w).len(); + } + } + // FIXME: clip position at top of file + self.pos = FilePosition::Fine(item, line); + self.ensure_enough_rendered(); + } } impl ActivityState for FeedFile { fn resize(&mut self, w: usize, h: usize) { - if self.last_width != Some(w) { - self.last_width = Some(w); + if self.last_size != Some((w, h)) { + self.last_size = Some((w, h)); self.coarsen_pos(); self.rendered.clear(); } - let (item, line) = self.refine_pos(w); - - let mut item = item; - let mut lines_rendered = line; - while item > self.contents.origin && lines_rendered + 1 < h { - item -= 1; - lines_rendered += self.ensure_item_rendered(item, w).len(); - } + self.ensure_enough_rendered(); } fn draw(&self, w: usize, h: usize) -> (Vec, CursorPosition) { - assert_eq!(self.last_width, Some(w), "resize() should have done that"); + assert_eq!(self.last_size, Some((w, h)), + "last resize() inconsistent with draw()"); let (start_item, start_line) = match self.pos { FilePosition::Fine(item, line) => (item, line), _ => panic!("coarse position reached draw()"), @@ -156,11 +192,16 @@ impl ActivityState for FeedFile { let mut item = start_item; let mut lines = Vec::new(); + let mut at_bottom = item == self.contents.last_index(); + // Retrieve rendered lines from the bottom of the window upwards 'outer: while item >= self.contents.origin && lines.len() + 1 < h { let rendered = self.rendered.get(&item) .expect("unrendered item reached draw()"); let line_limit = if item == start_item { + if start_line != rendered.len() { + at_bottom = false; + } start_line } else { rendered.len() @@ -180,13 +221,41 @@ impl ActivityState for FeedFile { lines.extend_from_slice(&BlankLine::render_static()); } - (lines, CursorPosition::None) // FIXME: status line + let fs = FileStatusLine::new(); + let fs = if at_bottom { + fs.add(Pr('-'), "Back", 99) + } else { + fs.add(Space, "Down", 99) + }; + let fs = fs.add(Pr('q'), "Exit", 100) + .finalise(); + // FIXME: document more keys + // FIXME: best-effort percentage calculation + lines.extend_from_slice(&fs.render(w)); + + (lines, CursorPosition::End) } - fn handle_keypress(&mut self, _key: OurKey, _client: &mut Client) -> + fn handle_keypress(&mut self, key: OurKey, _client: &mut Client) -> LogicalAction { - LogicalAction::Nothing // FIXME + let (_w, h) = match self.last_size { + Some(size) => size, + None => panic!("handle_keypress before resize"), + }; + + match key { + Pr('q') | Pr('Q') => LogicalAction::Pop, + Up => { + self.move_up(1); + LogicalAction::Nothing + }, + Pr('-') | Pr('b') | Pr('B') | PgUp | Left => { + self.move_up(max(1, h - min(h, 3))); + LogicalAction::Nothing + }, + _ => LogicalAction::Nothing, + } } } diff --git a/src/menu.rs b/src/menu.rs index 18c4fe4..b26d494 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -52,7 +52,7 @@ impl Menu { let status = if is_main { status.message("Select an option") } else { - status.add(OurKey::Return, "Back", 10) + status.add(Return, "Back", 10) }; let mut menu = Menu { @@ -63,7 +63,7 @@ impl Menu { }; if !is_main { - menu.actions.insert(OurKey::Return, LogicalAction::Pop); + menu.actions.insert(Return, LogicalAction::Pop); } menu diff --git a/src/text.rs b/src/text.rs index 1d46e5d..04f8d6b 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1096,6 +1096,7 @@ fn key_to_string(key: OurKey) -> String { OurKey::Backspace => "BS".to_string(), // likely won't come up OurKey::Return => "RET".to_string(), OurKey::Escape => "ESC".to_string(), + OurKey::Space => "SPACE".to_string(), OurKey::Up => "Up".to_string(), OurKey::Down => "Down".to_string(), OurKey::Left => "Left".to_string(), diff --git a/src/tui.rs b/src/tui.rs index 1fab0f7..a595f7c 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -141,7 +141,7 @@ pub enum OurKey { Pr(char), Ctrl(char), FunKey(u8), - Backspace, Return, Escape, + Backspace, Return, Escape, Space, Up, Down, Left, Right, PgUp, PgDn, Home, End, Ins, Del, } @@ -253,6 +253,7 @@ impl Tui { Some(OurKey::Ctrl('H')) => Some(OurKey::Backspace), Some(OurKey::Ctrl('M')) => Some(OurKey::Return), Some(OurKey::Ctrl('[')) => Some(OurKey::Escape), + Some(OurKey::Pr(' ')) => Some(OurKey::Space), other => other, } },