diff --git a/rtv/content_generators.py b/rtv/content_generators.py index d335e9a..8a6c802 100644 --- a/rtv/content_generators.py +++ b/rtv/content_generators.py @@ -16,7 +16,6 @@ class BaseContent(object): data['object'] = comment data['level'] = comment.nested_level - if isinstance(comment, praw.objects.MoreComments): data['type'] = 'MoreComments' data['body'] = 'More comments [{}]'.format(comment.count) @@ -147,8 +146,9 @@ class SubredditContent(BaseContent): self._submissions = self.r.get_front_page(limit=None) self.display_name = 'Front Page' else: - self._submissions = self.r.get_subreddit(self.subreddit, limit=None) - self.display_name = self._submissions.display_name + sub = self.r.get_subreddit(self.subreddit) + self._submissions = sub.get_hot() + self.display_name = '/r/' + self.subreddit class SubmissionContent(BaseContent): diff --git a/rtv/submission_viewer.py b/rtv/submission_viewer.py index 645cfbf..2ebfdd0 100644 --- a/rtv/submission_viewer.py +++ b/rtv/submission_viewer.py @@ -37,7 +37,7 @@ class SubmissionViewer(BaseViewer): self.draw() # Go back - elif cmd in (ord('b'), curses.KEY_LEFT): + elif cmd in (ord('b'), 27, curses.KEY_LEFT): break # Quit @@ -47,6 +47,13 @@ class SubmissionViewer(BaseViewer): else: curses.beep() + def refresh_content(self): + + self.add_loading() + self.content.reset() + self.stdscr.clear() + self.draw() + def draw(self): n_rows, n_cols = self.stdscr.getmaxyx() diff --git a/rtv/subreddit_viewer.py b/rtv/subreddit_viewer.py index 859e7ec..0ef35a2 100644 --- a/rtv/subreddit_viewer.py +++ b/rtv/subreddit_viewer.py @@ -6,7 +6,7 @@ import sys from content_generators import SubredditContent, SubmissionContent from submission_viewer import SubmissionViewer from viewer import BaseViewer -from utils import curses_session +from utils import curses_session, text_input class SubredditViewer(BaseViewer): @@ -31,7 +31,7 @@ class SubredditViewer(BaseViewer): # Enter edit mode to change subreddit elif cmd == ord('/'): - pass + self.prompt_subreddit() # Refresh page elif cmd in (curses.KEY_F5, ord('r')): @@ -47,6 +47,29 @@ class SubredditViewer(BaseViewer): else: curses.beep() + def refresh_content(self, subreddit=None): + + self.add_loading() + self.nav.page_index, self.nav.cursor_index = 0, 0 + self.nav.inverted = False + self.content.reset(subreddit=subreddit) + self.stdscr.clear() + self.draw() + + def prompt_subreddit(self): + + prompt = 'Enter Subreddit: /r/' + n_rows, n_cols = self.stdscr.getmaxyx() + self.stdscr.addstr(n_rows-1, 0, prompt) + self.stdscr.refresh() + window = self.stdscr.derwin(n_rows-1, len(prompt)) + + out = text_input(window) + if out is None: + self.draw() + else: + self.refresh_content(subreddit=out) + def open_submission(self): "Select the current submission to view posts" diff --git a/rtv/utils.py b/rtv/utils.py index 852bab2..f49b873 100644 --- a/rtv/utils.py +++ b/rtv/utils.py @@ -1,7 +1,12 @@ from datetime import datetime, timedelta from contextlib import contextmanager - +import os import curses +from curses import textpad + +class EscapePressed(Exception): + pass + def clean(unicode_string): """ @@ -46,10 +51,51 @@ def humanize_timestamp(utc_timestamp, verbose=False): years = months / 12 return ('%d years ago' % years) if verbose else ('%dyr' % years) + +def validate(ch): + "Filters characters for special key sequences" + if ch == 27: + raise EscapePressed + return ch + +def text_input(window): + """ + Transform a window into a text box that will accept user input and loop + until an escape sequence is entered. + + If enter is pressed, return the input text as a string. + If escape is pressed, return None. + """ + + window.clear() + curses.curs_set(2) + textbox = textpad.Textbox(window, insert_mode=True) + + # Wrapping in an exception block so that we can distinguish when the user + # hits the return character from when the user tries to back out of the + # input. + try: + out = textbox.edit(validate=validate) + out = out.strip() + except EscapePressed: + out = None + + curses.curs_set(0) + return out + @contextmanager def curses_session(): try: + # Curses must wait for some time after the Escape key is pressed to see + # check if it is the beginning of an escape sequence indicating a + # special key. The default wait time is 1 second, which means that + # getch() will not return the escape key (ord(27)), until a full second + # after it has been pressed. Turn this down to 25 ms, which is close to + # what VIM uses. + # http://stackoverflow.com/questions/27372068 + os.environ['ESCDELAY'] = '25' + # Initialize curses stdscr = curses.initscr() diff --git a/rtv/viewer.py b/rtv/viewer.py index a9541a8..2371da6 100644 --- a/rtv/viewer.py +++ b/rtv/viewer.py @@ -83,6 +83,7 @@ class BaseViewer(object): self.nav = Navigator(self.content.get, **kwargs) self._subwindows = None + self.add_loading() def draw(self): raise NotImplementedError @@ -106,12 +107,6 @@ class BaseViewer(object): continue self.stdscr.nodelay(0) - def refresh_content(self): - self.add_loading() - self.content.reset() - self.stdscr.clear() - self.draw() - def add_loading(self): "Draw a `loading` popup dialog in the center of the screen" @@ -132,7 +127,6 @@ class BaseViewer(object): self._header_window.erase() self._header_window.addnstr(0, 0, self.content.display_name, n_cols-1) - self._header_window.refresh() def draw_content(self): """