Merge pull request #217 from michael-lazar/cursor-tracking

Cursor tracking
This commit is contained in:
Michael Lazar
2016-04-21 01:15:44 -07:00
8 changed files with 270 additions and 17 deletions

View File

@@ -92,6 +92,7 @@ class Content(object):
data['type'] = 'MoreComments' data['type'] = 'MoreComments'
data['count'] = comment.count data['count'] = comment.count
data['body'] = 'More comments' data['body'] = 'More comments'
data['hidden'] = True
else: else:
author = getattr(comment, 'author', '[deleted]') author = getattr(comment, 'author', '[deleted]')
name = getattr(author, 'name', '[deleted]') name = getattr(author, 'name', '[deleted]')
@@ -114,6 +115,7 @@ class Content(object):
data['gold'] = comment.gilded > 0 data['gold'] = comment.gilded > 0
data['permalink'] = permalink data['permalink'] = permalink
data['stickied'] = stickied data['stickied'] = stickied
data['hidden'] = False
return data return data
@@ -154,6 +156,7 @@ class Content(object):
data['gold'] = sub.gilded > 0 data['gold'] = sub.gilded > 0
data['nsfw'] = sub.over_18 data['nsfw'] = sub.over_18
data['stickied'] = sub.stickied data['stickied'] = sub.stickied
data['hidden'] = False
data['index'] = None # This is filled in later by the method caller data['index'] = None # This is filled in later by the method caller
if sub.url.split('/r/')[-1] == sub.permalink.split('/r/')[-1]: if sub.url.split('/r/')[-1] == sub.permalink.split('/r/')[-1]:
@@ -317,7 +320,8 @@ class SubmissionContent(Content):
'cache': cache, 'cache': cache,
'count': count, 'count': count,
'level': data['level'], 'level': data['level'],
'body': 'Hidden'} 'body': 'Hidden',
'hidden': True}
self._comment_data[index:index + len(cache)] = [comment] self._comment_data[index:index + len(cache)] = [comment]

View File

@@ -315,7 +315,8 @@ class Navigator(object):
valid_page_cb, valid_page_cb,
page_index=0, page_index=0,
cursor_index=0, cursor_index=0,
inverted=False): inverted=False,
top_item_height=None):
""" """
Params: Params:
valid_page_callback (func): This function, usually `Content.get`, valid_page_callback (func): This function, usually `Content.get`,
@@ -331,11 +332,16 @@ class Navigator(object):
inverted - The page is drawn from the bottom of the screen, inverted - The page is drawn from the bottom of the screen,
starting with the page index, up to the top of the starting with the page index, up to the top of the
screen. screen.
top_item_height (int): If this is set to a non-null value
The number of columns that the top-most item
should utilize if non-inverted. This is used for a special mode
where all items are drawn non-inverted except for the top one.
""" """
self.page_index = page_index self.page_index = page_index
self.cursor_index = cursor_index self.cursor_index = cursor_index
self.inverted = inverted self.inverted = inverted
self.top_item_height = top_item_height
self._page_cb = valid_page_cb self._page_cb = valid_page_cb
@property @property
@@ -396,15 +402,21 @@ class Navigator(object):
# Flip the orientation and reset the cursor # Flip the orientation and reset the cursor
self.flip(self.cursor_index) self.flip(self.cursor_index)
self.cursor_index = 0 self.cursor_index = 0
self.top_item_height = None
redraw = True redraw = True
else: else:
if self.cursor_index > 0: if self.cursor_index > 0:
self.cursor_index -= 1 self.cursor_index -= 1
if self.top_item_height and self.cursor_index == 0:
# Selecting the partially displayed item
self.top_item_height = None
redraw = True
else: else:
self.page_index -= self.step self.page_index -= self.step
if self._is_valid(self.absolute_index): if self._is_valid(self.absolute_index):
# We have reached the beginning of the page - move the # We have reached the beginning of the page - move the
# index # index
self.top_item_height = None
redraw = True redraw = True
else: else:
self.page_index += self.step self.page_index += self.step
@@ -478,6 +490,7 @@ class Navigator(object):
self.page_index += (self.step * n_windows) self.page_index += (self.step * n_windows)
self.cursor_index = n_windows self.cursor_index = n_windows
self.inverted = not self.inverted self.inverted = not self.inverted
self.top_item_height = None
def _is_valid(self, page_index): def _is_valid(self, page_index):
""" """

View File

@@ -327,29 +327,46 @@ class Page(object):
# If not inverted, align the first submission with the top and draw # If not inverted, align the first submission with the top and draw
# downwards. If inverted, align the first submission with the bottom # downwards. If inverted, align the first submission with the bottom
# and draw upwards. # and draw upwards.
cancel_inverted = True
current_row = (win_n_rows - 1) if inverted else 0 current_row = (win_n_rows - 1) if inverted else 0
available_rows = (win_n_rows - 1) if inverted else win_n_rows available_rows = (win_n_rows - 1) if inverted else win_n_rows
top_item_height = None if inverted else self.nav.top_item_height
for data in self.content.iterate(page_index, step, win_n_cols - 2): for data in self.content.iterate(page_index, step, win_n_cols - 2):
subwin_n_rows = min(available_rows, data['n_rows']) subwin_n_rows = min(available_rows, data['n_rows'])
subwin_inverted = inverted
if top_item_height is not None:
# Special case: draw the page as non-inverted, except for the
# top element. This element will be drawn as inverted with a
# restricted height
subwin_n_rows = min(subwin_n_rows, top_item_height)
subwin_inverted = True
top_item_height = None
subwin_n_cols = win_n_cols - data['offset'] subwin_n_cols = win_n_cols - data['offset']
start = current_row - subwin_n_rows if inverted else current_row start = current_row - subwin_n_rows if inverted else current_row
subwindow = window.derwin( subwindow = window.derwin(
subwin_n_rows, subwin_n_cols, start, data['offset']) subwin_n_rows, subwin_n_cols, start, data['offset'])
attr = self._draw_item(subwindow, data, inverted) attr = self._draw_item(subwindow, data, subwin_inverted)
self._subwindows.append((subwindow, attr)) self._subwindows.append((subwindow, attr))
available_rows -= (subwin_n_rows + 1) # Add one for the blank line available_rows -= (subwin_n_rows + 1) # Add one for the blank line
current_row += step * (subwin_n_rows + 1) current_row += step * (subwin_n_rows + 1)
if available_rows <= 0: if available_rows <= 0:
# Indicate the page is full and we can keep the inverted screen.
cancel_inverted = False
break break
else:
# If the page is not full we need to make sure that it is NOT if len(self._subwindows) == 1:
# Never draw inverted if only one subwindow. The top of the
# subwindow should always be aligned with the top of the screen.
cancel_inverted = True
if cancel_inverted and self.nav.inverted:
# In some cases we need to make sure that the screen is NOT
# inverted. Unfortunately, this currently means drawing the whole # inverted. Unfortunately, this currently means drawing the whole
# page over again. Could not think of a better way to pre-determine # page over again. Could not think of a better way to pre-determine
# if the content will fill up the page, given that it is dependent # if the content will fill up the page, given that it is dependent
# on the size of the terminal. # on the size of the terminal.
if self.nav.inverted: self.nav.flip((len(self._subwindows) - 1))
self.nav.flip((len(self._subwindows) - 1)) self._draw_content()
self._draw_content()
self._row = n_rows self._row = n_rows

View File

@@ -33,11 +33,19 @@ class SubmissionPage(Page):
current_index = self.nav.absolute_index current_index = self.nav.absolute_index
self.content.toggle(current_index) self.content.toggle(current_index)
# This logic handles a display edge case after a comment toggle. We want
# to make sure that when we re-draw the page, the cursor stays at its
# current absolute position on the screen. In order to do this, apply
# a fixed offset if, while inverted, we either try to hide the bottom
# comment or toggle any of the middle comments.
if self.nav.inverted: if self.nav.inverted:
# Reset the navigator so that the cursor is at the bottom of the data = self.content.get(current_index)
# page. This is a workaround to handle if folding the comment if data['hidden'] or self.nav.cursor_index != 0:
# causes the cursor index to go out of bounds. window = self._subwindows[-1][0]
self.nav.page_index, self.nav.cursor_index = current_index, 0 n_rows, _ = window.getmaxyx()
self.nav.flip(len(self._subwindows) - 1)
self.nav.top_item_height = n_rows
@SubmissionController.register(Command('SUBMISSION_EXIT')) @SubmissionController.register(Command('SUBMISSION_EXIT'))
def exit_submission(self): def exit_submission(self):
@@ -155,6 +163,16 @@ class SubmissionPage(Page):
valid_rows = range(0, n_rows) valid_rows = range(0, n_rows)
offset = 0 if not inverted else -(data['n_rows'] - n_rows) offset = 0 if not inverted else -(data['n_rows'] - n_rows)
# If there isn't enough space to fit the comment body on the screen,
# replace the last line with a notification.
split_body = data['split_body']
if data['n_rows'] > n_rows:
# Only when there is a single comment on the page and not inverted
if not inverted and len(self._subwindows) == 0:
cutoff = data['n_rows'] - n_rows + 1
split_body = split_body[:-cutoff]
split_body.append('(Not enough space to display)')
row = offset row = offset
if row in valid_rows: if row in valid_rows:
@@ -178,7 +196,7 @@ class SubmissionPage(Page):
text, attr = self.term.stickied text, attr = self.term.stickied
self.term.add_line(win, text, attr=attr) self.term.add_line(win, text, attr=attr)
for row, text in enumerate(data['split_body'], start=offset+1): for row, text in enumerate(split_body, start=offset+1):
if row in valid_rows: if row in valid_rows:
self.term.add_line(win, text, row, 1) self.term.add_line(win, text, row, 1)

View File

@@ -0,0 +1,173 @@
interactions:
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate, compress']
User-Agent: [!!python/unicode rtv test suite PRAW/3.3.0 Python/2.7.6 Linux-3.13.0-24-generic-x86_64-with-Ubuntu-14.04-trusty]
method: !!python/unicode GET
uri: https://www.reddit.com/r/Python/comments/2xmo63.json
response:
body:
string: !!binary |
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
headers:
access-control-allow-origin: ['*']
access-control-expose-headers: ['X-Reddit-Tracking, X-Moose']
cache-control: ['max-age=0, must-revalidate']
cf-ray: [296f3f0aeb072858-SJC]
connection: [keep-alive]
content-encoding: [gzip]
content-length: ['7683']
content-type: [application/json; charset=UTF-8]
date: ['Thu, 21 Apr 2016 07:50:39 GMT']
server: [cloudflare-nginx]
set-cookie: ['__cfduid=d8e1d83c8cfa76ac404e21fc7d924dc961461225038; expires=Fri,
21-Apr-17 07:50:38 GMT; path=/; domain=.reddit.com; HttpOnly', 'loid=hCXBgtTCOflyUpYHv9;
Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 21-Apr-2018 07:50:38
GMT; secure', 'loidcreated=2016-04-21T07%3A50%3A38.582Z; Domain=reddit.com;
Max-Age=63071999; Path=/; expires=Sat, 21-Apr-2018 07:50:38 GMT; secure']
strict-transport-security: [max-age=15552000; includeSubDomains; preload]
vary: [accept-encoding]
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-moose: [majestic]
x-reddit-tracking: ['https://pixel.redditmedia.com/pixel/of_destiny.png?v=wUoXo%2B%2BbEgovlSTbKrPiRzy5CGl5PaWJAwmKAdLtVFDt9zGb%2FmDNZzDIUNfJZ7SEiQl6UK0WZns%3D']
x-ua-compatible: [IE=edge]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View File

@@ -101,7 +101,8 @@ def test_content_submission(reddit, terminal):
assert content.get(40)['type'] == 'Comment' assert content.get(40)['type'] == 'Comment'
for data in content.iterate(-1, 1): for data in content.iterate(-1, 1):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type')) assert all(k in data for k in ('object', 'n_rows', 'offset', 'type',
'hidden'))
# All text should be converted to unicode by this point # All text should be converted to unicode by this point
for val in data.values(): for val in data.values():
assert not isinstance(val, six.binary_type) assert not isinstance(val, six.binary_type)
@@ -121,11 +122,14 @@ def test_content_submission(reddit, terminal):
data = content.get(2) data = content.get(2)
assert data['type'] == 'HiddenComment' assert data['type'] == 'HiddenComment'
assert data['count'] == 3 assert data['count'] == 3
assert data['hidden'] is True
assert data['level'] >= content.get(3)['level'] assert data['level'] >= content.get(3)['level']
assert len(content._comment_data) == 43 assert len(content._comment_data) == 43
# Toggling again expands the children # Toggling again expands the children
content.toggle(2) content.toggle(2)
data = content.get(2)
assert data['hidden'] is False
assert len(content._comment_data) == 45 assert len(content._comment_data) == 45
@@ -195,8 +199,9 @@ def test_content_subreddit(reddit, terminal):
assert content.get(0)['type'] == 'Submission' assert content.get(0)['type'] == 'Submission'
for data in content.iterate(0, 1): for data in content.iterate(0, 1):
assert all(k in data for k in ('object', 'n_rows', 'offset', 'type', assert all(k in data for k in (
'index', 'title', 'split_title')) 'object', 'n_rows', 'offset', 'type', 'index', 'title',
'split_title', 'hidden'))
# All text should be converted to unicode by this point # All text should be converted to unicode by this point
for val in data.values(): for val in data.values():
assert not isinstance(val, six.binary_type) assert not isinstance(val, six.binary_type)
@@ -227,6 +232,7 @@ def test_content_subreddit_load_more(reddit, terminal):
assert data['index'] == i + 1 assert data['index'] == i + 1
assert data['title'].startswith(six.text_type(i + 1)) assert data['title'].startswith(six.text_type(i + 1))
def test_content_subreddit_from_name(reddit, terminal): def test_content_subreddit_from_name(reddit, terminal):
name = '/r/python' name = '/r/python'

View File

@@ -346,11 +346,13 @@ def test_objects_navigator_properties():
assert nav.step == 1 assert nav.step == 1
assert nav.position == (0, 0, False) assert nav.position == (0, 0, False)
assert nav.absolute_index == 0 assert nav.absolute_index == 0
assert nav.top_item_height is None
nav = Navigator(valid_page_cb, 5, 2, True) nav = Navigator(valid_page_cb, 5, 2, True, 10)
assert nav.step == -1 assert nav.step == -1
assert nav.position == (5, 2, True) assert nav.position == (5, 2, True)
assert nav.absolute_index == 3 assert nav.absolute_index == 3
assert nav.top_item_height == 10
def test_objects_navigator_move(): def test_objects_navigator_move():
@@ -360,6 +362,7 @@ def test_objects_navigator_move():
raise IndexError() raise IndexError()
nav = Navigator(valid_page_cb) nav = Navigator(valid_page_cb)
nav.top_item_height = 5
# Try to scroll up past the first item # Try to scroll up past the first item
valid, redraw = nav.move(-1, 2) valid, redraw = nav.move(-1, 2)
@@ -375,6 +378,7 @@ def test_objects_navigator_move():
# Scroll down, reach last item on the page and flip the screen # Scroll down, reach last item on the page and flip the screen
valid, redraw = nav.move(1, 3) valid, redraw = nav.move(1, 3)
assert nav.top_item_height is None
assert nav.page_index == 2 assert nav.page_index == 2
assert nav.cursor_index == 0 assert nav.cursor_index == 0
assert nav.inverted assert nav.inverted

View File

@@ -111,6 +111,24 @@ def test_submission_pager(submission_page, terminal):
assert terminal.open_pager.called assert terminal.open_pager.called
def test_submission_comment_not_enough_space(submission_page, terminal):
# The first comment is 10 lines, shrink the screen so that it won't fit.
# Setting the terminal to 10 lines means that there will only be 8 lines
# available (after subtracting the header and footer) to draw the comment.
terminal.stdscr.nlines = 10
# Select the first comment
with mock.patch.object(submission_page, 'clear_input_queue'):
submission_page.move_cursor_down()
submission_page.draw()
text = '(Not enough space to display)'.encode('ascii')
window = terminal.stdscr.subwin
window.subwin.addstr.assert_any_call(7, 1, text)
def test_submission_vote(submission_page, refresh_token): def test_submission_vote(submission_page, refresh_token):
# Log in # Log in