import client
import text
+import util
class CursesUI(client.Client):
def curses_setup(self):
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(),
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()
self.lines = None
self.linepos = None
+ self.width = None
def iter_text_indexed(self):
yield self.header, None
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))
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("")
# 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:
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):