commit f9cf4bbc821356f7918860166d23f682ece39846
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Apr 3 12:45:31 2016 -0700
Revise sort order dialog
Rewriting this sucker. Quite a bit shorter, cleaner, and now tested.
---
nyx/popups.py | 145 ++++++++++++++++++++-----------------------------------
test/__init__.py | 2 +-
test/popups.py | 62 ++++++++++++++++++++++++
3 files changed, 116 insertions(+), 93 deletions(-)
diff --git a/nyx/popups.py b/nyx/popups.py
index 4a51e0c..b0cd48e 100644
--- a/nyx/popups.py
+++ b/nyx/popups.py
@@ -199,114 +199,75 @@ def show_counts(title, counts, fill_char = ' '):
nyx.curses.key_input()
-def show_sort_dialog(title, options, old_selection, option_colors):
+def show_sort_dialog(title, options, previous_order, option_colors):
"""
- Displays a sorting dialog of the form:
+ Provides sorting dialog of the form...
- Current Order: <previous selection>
- New Order: <selections made>
+ Current Order: <previous order>
+ New Order: <selected options>
<option 1> <option 2> <option 3> Cancel
- Options are colored when among the "Current Order" or "New Order", but not
- when an option below them. If cancel is selected or the user presses escape
- then this returns None. Otherwise, the new ordering is provided.
+ :param str title: dialog title
+ :param list options: sort options to be provided
+ :param list previous_order: previous ordering
+ :param dict optoin_colors: mapping of options to their color
- Arguments:
- title - title displayed for the popup window
- options - ordered listing of option labels
- old_selection - current ordering
- option_colors - mappings of options to their color
+ :returns: **list** of the new sort order or **None** if dialog is canceled
"""
- with popup_window(9, 80) as (popup, _, _):
- if popup:
- new_selections = [] # new ordering
- cursor_location = 0 # index of highlighted option
-
- selection_options = list(options)
- selection_options.append('Cancel')
-
- while len(new_selections) < len(old_selection):
- popup.win.erase()
- popup.draw_box()
- popup.addstr(0, 0, title, HIGHLIGHT)
-
- _draw_sort_selection(popup, 1, 2, 'Current Order: ', old_selection, option_colors)
- _draw_sort_selection(popup, 2, 2, 'New Order: ', new_selections, option_colors)
-
- # presents remaining options, each row having up to four options with
- # spacing of nineteen cells
-
- row, col = 4, 0
-
- for i in range(len(selection_options)):
- option_format = HIGHLIGHT if cursor_location == i else NORMAL
- popup.addstr(row, col * 19 + 2, selection_options[i], option_format)
- col += 1
-
- if col == 4:
- row, col = row + 1, 0
-
- popup.win.refresh()
-
- key = nyx.curses.key_input()
-
- if key.match('left'):
- cursor_location = max(0, cursor_location - 1)
- elif key.match('right'):
- cursor_location = min(len(selection_options) - 1, cursor_location + 1)
- elif key.match('up'):
- cursor_location = max(0, cursor_location - 4)
- elif key.match('down'):
- cursor_location = min(len(selection_options) - 1, cursor_location + 4)
- elif key.is_selection():
- selection = selection_options[cursor_location]
-
- if selection == 'Cancel':
- break
- else:
- new_selections.append(selection)
- selection_options.remove(selection)
- cursor_location = min(cursor_location, len(selection_options) - 1)
- elif key.match('esc'):
- break # esc - cancel
+ new_order = []
+ cursor_index = 0
+ shown_options = list(options) + ['Cancel']
- if len(new_selections) == len(old_selection):
- return new_selections
- else:
- return None
+ def _draw_selection(subwindow, y, label, selection):
+ x = subwindow.addstr(2, y, label, BOLD)
+ for i, option in enumerate(selection):
+ x = subwindow.addstr(x, y, option, option_colors.get(option, WHITE), BOLD)
-def _draw_sort_selection(popup, y, x, prefix, options, option_colors):
- """
- Draws a series of comma separated sort selections. The whole line is bold
- and sort options also have their specified color. Example:
+ if i < len(selection) - 1:
+ x = subwindow.addstr(x, y, ', ', BOLD)
- Current Order: Man Page Entry, Option Name, Is Default
-
- Arguments:
- popup - panel in which to draw sort selection
- y - vertical location
- x - horizontal location
- prefix - initial string description
- options - sort options to be shown
- option_colors - mappings of options to their color
- """
+ def _render(subwindow):
+ subwindow.box()
+ subwindow.addstr(0, 0, title, HIGHLIGHT)
- popup.addstr(y, x, prefix, BOLD)
- x += len(prefix)
+ _draw_selection(subwindow, 1, 'Current Order: ', previous_order)
+ _draw_selection(subwindow, 2, 'New Order: ', new_order)
- for i in range(len(options)):
- sort_type = options[i]
- popup.addstr(y, x, sort_type, option_colors.get(sort_type, WHITE), BOLD)
- x += len(sort_type)
+ # presents remaining options, each row having up to four options
- # comma divider between options, if this isn't the last
+ for i, option in enumerate(shown_options):
+ attr = HIGHLIGHT if i == cursor_index else NORMAL
+ subwindow.addstr((i % 4) * 19 + 2, (i / 4) + 4, option, attr)
- if i < len(options) - 1:
- popup.addstr(y, x, ', ', BOLD)
- x += 2
+ with nyx.curses.CURSES_LOCK:
+ while len(new_order) < len(previous_order):
+ nyx.curses.draw(_render, top = nyx.controller.get_controller().header_panel().get_height(), width = 80, height = 9)
+ key = nyx.curses.key_input()
+
+ if key.match('left'):
+ cursor_index = max(0, cursor_index - 1)
+ elif key.match('right'):
+ cursor_index = min(len(shown_options) - 1, cursor_index + 1)
+ elif key.match('up'):
+ cursor_index = max(0, cursor_index - 4)
+ elif key.match('down'):
+ cursor_index = min(len(shown_options) - 1, cursor_index + 4)
+ elif key.is_selection():
+ selection = shown_options[cursor_index]
+
+ if selection == 'Cancel':
+ return None
+ else:
+ new_order.append(selection)
+ shown_options.remove(selection)
+ cursor_index = min(cursor_index, len(shown_options) - 1)
+ elif key.match('esc'):
+ return None
+
+ return new_order
def show_menu(title, options, old_selection):
diff --git a/test/__init__.py b/test/__init__.py
index 0884866..705e9b1 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -51,7 +51,7 @@ def render(func, *args, **kwargs):
if SHOW_RENDERED_CONTENT:
time.sleep(SHOW_RENDERED_CONTENT)
- with patch('nyx.curses.key_input', return_value = Mock()):
+ with patch('nyx.curses.key_input', return_value = nyx.curses.KeyInput(27)):
nyx.curses.start(draw_func, transparent_background = True, cursor = False)
return RenderResult(attr.get('content'), attr.get('return_value'), attr.get('runtime'))
diff --git a/test/popups.py b/test/popups.py
index b82c9a2..8a7e76b 100644
--- a/test/popups.py
+++ b/test/popups.py
@@ -2,6 +2,7 @@
Unit tests for nyx.popups.
"""
+import curses
import unittest
import nyx.panel
@@ -52,6 +53,30 @@ Client Locales-----------------------------------------------------------------+
+------------------------------------------------------------------------------+
""".strip()
+EXPECTED_SORT_DIALOG_START = """
+Config Option Ordering:--------------------------------------------------------+
+| Current Order: Man Page Entry, Name, Is Set |
+| New Order: |
+| |
+| Name Value Value Type Category |
+| Usage Summary Description Man Page Entry |
+| Is Set Cancel |
+| |
++------------------------------------------------------------------------------+
+""".strip()
+
+EXPECTED_SORT_DIALOG_END = """
+Config Option Ordering:--------------------------------------------------------+
+| Current Order: Man Page Entry, Name, Is Set |
+| New Order: Name, Summary |
+| |
+| Value Value Type Category Usage |
+| Description Man Page Entry Is Set Cancel |
+| |
+| |
++------------------------------------------------------------------------------+
+""".strip()
+
class TestPopups(unittest.TestCase):
@patch('nyx.controller.get_controller')
@@ -117,3 +142,40 @@ class TestPopups(unittest.TestCase):
rendered = test.render(nyx.popups.show_counts, 'Client Locales', clients, fill_char = '*')
self.assertEqual(EXPECTED_COUNTS, rendered.content)
+
+ @patch('nyx.controller.get_controller')
+ def test_sort_dialog(self, get_controller_mock):
+ get_controller_mock().header_panel().get_height.return_value = 0
+
+ previous_order = ['Man Page Entry', 'Name', 'Is Set']
+ options = ['Name', 'Value', 'Value Type', 'Category', 'Usage', 'Summary', 'Description', 'Man Page Entry', 'Is Set']
+
+ rendered = test.render(nyx.popups.show_sort_dialog, 'Config Option Ordering:', options, previous_order, {})
+ self.assertEqual(EXPECTED_SORT_DIALOG_START, rendered.content)
+ self.assertEqual(None, rendered.return_value)
+
+ @patch('nyx.controller.get_controller')
+ def test_sort_dialog_selecting(self, get_controller_mock):
+ # Use the dialog to make a selection. At the end we render two options as
+ # being selected (rather than three) because the act of selecing the third
+ # closed the popup.
+
+ keypresses = [
+ nyx.curses.KeyInput(curses.KEY_ENTER),
+ nyx.curses.KeyInput(curses.KEY_DOWN),
+ nyx.curses.KeyInput(curses.KEY_ENTER),
+ nyx.curses.KeyInput(curses.KEY_ENTER),
+ ]
+
+ def draw_func():
+ with patch('nyx.curses.key_input', side_effect = keypresses):
+ return nyx.popups.show_sort_dialog('Config Option Ordering:', options, previous_order, {})
+
+ get_controller_mock().header_panel().get_height.return_value = 0
+
+ previous_order = ['Man Page Entry', 'Name', 'Is Set']
+ options = ['Name', 'Value', 'Value Type', 'Category', 'Usage', 'Summary', 'Description', 'Man Page Entry', 'Is Set']
+
+ rendered = test.render(draw_func)
+ self.assertEqual(EXPECTED_SORT_DIALOG_END, rendered.content)
+ self.assertEqual(['Name', 'Summary', 'Description'], rendered.return_value)