From 444d2cfe6644265c911bf0bb0b6d07cf71d8f82d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 5 Dec 2023 13:05:23 +0000 Subject: [PATCH] Paging around a file now works! --- cursesclient.py | 74 +++++++++++++++++++++++++++++++++++++------------ text.py | 37 +++++++++++++++---------- util.py | 9 ++++++ 3 files changed, 89 insertions(+), 31 deletions(-) diff --git a/cursesclient.py b/cursesclient.py index 837edb6..489e5e9 100644 --- a/cursesclient.py +++ b/cursesclient.py @@ -4,6 +4,7 @@ import sys import client import text +import util class CursesUI(client.Client): def curses_setup(self): @@ -83,6 +84,10 @@ class CursesUI(client.Client): for y in range(self.scr_h): self.print_at(y, 0, text.ColouredString(' ' * self.scr_w)) + def get_input(self): + # FIXME: add a select loop for self-pipes + return self.scr.getch() + def run(self): self.home_timeline = StatusFile( self, self.home_timeline_feed(), @@ -91,9 +96,29 @@ class CursesUI(client.Client): try: self.curses_setup() - self.home_timeline.render() - self.scr.refresh() - self.scr.getch() + + while True: + self.clear() + self.home_timeline.render() + self.scr.refresh() + ch = self.get_input() + if ch in {ord(' '), curses.KEY_NPAGE}: + self.home_timeline.down_screen() + elif ch in {ord('-'), ord('b'), ord('B'), curses.KEY_PPAGE}: + self.home_timeline.up_screen() + elif ch in {13, curses.KEY_DOWN}: + self.home_timeline.down_line() + elif ch in {curses.KEY_UP}: + self.home_timeline.up_line() + elif ch in {ord('0'), curses.KEY_HOME}: + self.home_timeline.goto_top() + elif ch in {ord('z'), ord('Z'), curses.KEY_END}: + self.home_timeline.goto_bottom() + elif ch in {ord('q'), ord('Q')}: + break + elif ch == curses.KEY_RESIZE: + self.scr_h, self.scr_w = self.scr.getmaxyx() + finally: self.curses_shutdown() @@ -114,6 +139,7 @@ class StatusFile: self.lines = None self.linepos = None + self.width = None def iter_text_indexed(self): yield self.header, None @@ -124,32 +150,46 @@ class StatusFile: yield thing, i # FIXME: maybe just yield the last? def resize(self, width): + if self.width == width: + return + self.width = width self.lines = [] - self.linepos = 0 + pos = 0 for thing, itemindex in self.iter_text_indexed(): for line in thing.render(width): - self.lines.append(line) + for s in line.split(width): + self.lines.append(s) if itemindex == self.itempos: - self.linepos = len(self.lines) + pos = len(self.lines) + self.move_to(pos) def render(self): - self.cc.clear() self.resize(self.cc.scr_w) - topline = max(0, self.linepos - self.cc.scr_w - 1) + topline = max(0, self.linepos - (self.cc.scr_h - 1)) for y, line in enumerate(self.lines[topline:topline+self.cc.scr_h-1]): self.cc.print_at(y, 0, line) sl = text.FileStatusLine() - sl.percentage = self.linepos / len(self.lines) - sl_rendered = next(sl.render(self.cc.scr_w)) - # FIXME: keys + if self.linepos >= len(self.lines): + sl.keys.append(('-', 'Up')) + else: + sl.keys.append(('SPACE', 'More')) + sl.keys.append(('Q', 'Exit')) + sl.proportion = self.linepos / len(self.lines) + sl_rendered = util.exactly_one(sl.render(self.cc.scr_w)) self.cc.print_at(self.cc.scr_h - 1, 0, sl_rendered) -class Activity: - pass + def move_to(self, pos): + self.linepos = pos + self.linepos = max(self.linepos, self.cc.scr_h - 1) + self.linepos = min(self.linepos, len(self.lines)) -class FileActivity(Activity): - pass + def move_by(self, delta): + self.move_to(self.linepos + delta) -class StatusFileActivity(FileActivity): - pass + def down_screen(self): self.move_by(self.cc.scr_h - 1) + def up_screen(self): self.move_by(-(self.cc.scr_h - 1)) + def down_line(self): self.move_by(+1) + def up_line(self): self.move_by(-1) + def goto_top(self): self.move_to(0) + def goto_bottom(self): self.move_to(len(self.lines)) diff --git a/text.py b/text.py index 4448548..c5e8183 100644 --- a/text.py +++ b/text.py @@ -94,6 +94,16 @@ class ColouredString: yield frag, colour, wcwidth.wcswidth(frag) pos += fraglen + def split(self, width): + # Split on to multiple physical lines. + line = ColouredString("") + for c in self: + if line.width + c.width > width: + yield line + line = ColouredString("") + line += c + yield line + class BlankLine: def render(self, width): yield ColouredString("") @@ -161,14 +171,15 @@ class FileHeader: # FIXME: truncate headertext = self.text - space = width - 2 * logowidth - 2 - headertext.width - lspace = space // 2 + 1 - rspace = space - lspace + 1 + midspace = width - 1 - 2 * logowidth - 2 + space = midspace - headertext.width + lspace = space // 2 + rspace = space - lspace - yield (logo[0] + ColouredString(" " * lspace) + - headertext + ColouredString(" " * rspace) + logo[0]) + yield (logo[0] + ColouredString(" " * (lspace+1)) + + headertext + ColouredString(" " * (rspace+1)) + logo[0]) yield (logo[1] + ColouredString(" ") + - ColouredString("~" * space, '~') + ColouredString(" ") + + ColouredString("~" * midspace, '~') + ColouredString(" ") + logo[1]) class ExtendableIndicator: @@ -191,24 +202,22 @@ class FileStatusLine: def render(self, width): message = ColouredString('') sep = ColouredString('') + sep2 = ColouredString(' ') for key, action in self.keys: message += ( sep + ColouredString('[') + ColouredString(key, 'k') + ColouredString(']:' + action)) - sep = ColouredString(' ') + sep = sep2 if self.proportion is not None: - message += ( - sep + ColouredString('({:d}%)'.format(self.proportion * 100))) - sep = ColouredString(' ') + message += sep + ColouredString('({:d}%)'.format( + int(self.proportion * 100))) + sep = sep2 space = width - message.width lspace = space // 2 + 1 rspace = space - lspace + 1 - yield ColouredString("") - yield (ColouredString(" " * lspace) + message + - ColouredString(" " * rspace)) - yield ColouredString("") + yield (ColouredString(" " * lspace) + message) class Paragraph: def __init__(self): diff --git a/util.py b/util.py index dcec58b..fcd6578 100644 --- a/util.py +++ b/util.py @@ -13,3 +13,12 @@ class SelfPipe: while len(os.read(self, 4096)) != 0: pass return True + +def exactly_one(stuff): + it = iter(stuff) + toret = next(it) + try: + next(it) + except StopIteration: + return toret + raise ValueError("exactly_one got more than one") -- 2.30.2