commit fd920dfd4fc88d14b7e569b2513b449cc63cae99 Author: Kamran Riaz Khan krkhan@inspirated.com Date: Sat May 28 18:29:15 2011 +0500
Support long top menus on smaller screens, code review fixes.
The top-menu now displays "remaining" entries when right/left arrow keys are pressed enough times on shorter screens.
Menus are no longer persistence and therefore are removed from Controller's init.
Code review fixes: * Menu.draw() is renamed to Menu.showMenu(). * Overzealous map()s are replaced in favor of list comprehensions. * Popups are drawn through popup.add*() methods instead of drawing to windows directly. --- src/cli/controller.py | 12 ++----- src/cli/menu.py | 78 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 30 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py index c506302..25c3051 100644 --- a/src/cli/controller.py +++ b/src/cli/controller.py @@ -81,7 +81,7 @@ def initController(stdscr, startTime): menu = cli.menu.Menu()
# initializes the controller - ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels, menu) + ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels)
# additional configuration for the graph panel graphPanel = ARM_CONTROLLER.getPanel("graph") @@ -135,7 +135,7 @@ class Controller: Tracks the global state of the interface """
- def __init__(self, stdscr, stickyPanels, pagePanels, menu): + def __init__(self, stdscr, stickyPanels, pagePanels): """ Creates a new controller instance. Panel lists are ordered as they appear, top to bottom on the page. @@ -150,7 +150,6 @@ class Controller: self._screen = stdscr self._stickyPanels = stickyPanels self._pagePanels = pagePanels - self._menu = menu self._page = 0 self._isPaused = False self._forceRedraw = False @@ -188,9 +187,6 @@ class Controller: self._forceRedraw = True self.setMsg()
- def getMenu(self): - return self._menu - def isPaused(self): """ True if the interface is paused, false otherwise. @@ -517,8 +513,8 @@ def drawTorMonitor(stdscr, startTime): elif key == ord('p') or key == ord('P'): control.setPaused(not control.isPaused()) elif key == ord('m') or key == ord('M'): - menu = control.getMenu() - menu.draw() + menu = cli.menu.Menu() + menu.showMenu() elif key == ord('q') or key == ord('Q'): # provides prompt to confirm that arm should exit if CONFIG["features.confirmQuit"]: diff --git a/src/cli/menu.py b/src/cli/menu.py index 0395abb..bedb98e 100644 --- a/src/cli/menu.py +++ b/src/cli/menu.py @@ -4,7 +4,6 @@ A drop-down menu for sending actions to panels.
import curses from collections import namedtuple -from operator import attrgetter
import cli.controller import popups @@ -25,16 +24,22 @@ class Menu(): LeafEntry(title="Connections" , callback=self._callbackDefault), LeafEntry(title="Configuration" , callback=self._callbackDefault)))
+ self._first = [0] self._selection = [0] - self._rootEntry = (entry and isinstance(entry, ParentEntry)) and entry or DEFAULT_ROOT
- def draw(self): + if entry and isinstance(entry, ParentEntry): + self._rootEntry = entry + else: + self._rootEntry = DEFAULT_ROOT + + def showMenu(self): popup, width, height = popups.init(height=3) if popup: try: - popup.win.box() - while True: + popup.win.erase() + popup.win.box() + self._drawTopLevel(popup, width, height)
popup.win.refresh() @@ -44,12 +49,10 @@ class Menu():
if key == curses.KEY_RIGHT: if len(self._selection) == 1: - # selection is on top menu - self._selection[0] = (self._selection[0] + 1) % len(self._rootEntry.children) + self._moveTopLevelRight(width) elif key == curses.KEY_LEFT: if len(self._selection) == 1: - # selection is on top menu - self._selection[0] = (self._selection[0] - 1) % len(self._rootEntry.children) + self._moveTopLevelLeft(width) elif uiTools.isSelectionKey(key): self._handleEvent() break @@ -57,40 +60,71 @@ class Menu(): finally: popups.finalize()
- def _drawTopLevel(self, popup, width, height): - titles = map(attrgetter('title'), self._rootEntry.children) + def _calculateTopLevelWidths(self, width): + titles = [menuItem.title for menuItem in self._rootEntry.children]
# width per title is set according to the longest title - titlewidth = max(map(lambda title: len(title), titles)) + 2 + titlewidth = max(map(len, titles)) + 2
# total number of titles that can be printed in current width - printable = width / titlewidth - 1 + printable = min(width / titlewidth - 1, len(self._rootEntry.children)) + + return (titlewidth, printable) + + def _moveTopLevelRight(self, width): + _, printable = self._calculateTopLevelWidths(width) + + if self._selection[0] < printable - 1: + self._selection[0] = self._selection[0] + 1 + else: + self._selection[0] = 0 + if printable < len(self._rootEntry.children): + self._first[0] = (self._first[0] + printable) % len(self._rootEntry.children) + + if self._first[0] + self._selection[0] == len(self._rootEntry.children): + self._first[0] = 0 + self._selection[0] = 0 + + def _moveTopLevelLeft(self, width): + _, printable = self._calculateTopLevelWidths(width) + + if self._selection[0] > 0: + self._selection[0] = self._selection[0] - 1 + else: + self._first[0] = abs(self._first[0] - printable) % len(self._rootEntry.children) + self._selection[0] = len(self._rootEntry.children) - self._first[0] - 1 + + if self._selection[0] > printable: + self._selection[0] = printable - 1 + + def _drawTopLevel(self, popup, width, height): + titlewidth, printable = self._calculateTopLevelWidths(width) + children = self._rootEntry.children[self._first[0]:self._first[0] + printable]
top = 1 left = 1 - for (index, entry) in enumerate(self._rootEntry.children[:printable]): - titleformat = curses.A_NORMAL - - if index == self._selection[0]: - titleformat = curses.A_STANDOUT + for (index, entry) in enumerate(children): + titleformat = curses.A_STANDOUT if index == self._selection[0] else curses.A_NORMAL
- popup.win.addch(top, left, curses.ACS_VLINE) + popup.addch(top, left, curses.ACS_VLINE) left = left + 1 - popup.win.addstr(top, left, entry.title.center(titlewidth), titleformat) + popup.addstr(top, left, entry.title.center(titlewidth), titleformat) left = left + titlewidth
- popup.win.addch(top, left, curses.ACS_VLINE) + popup.addch(top, left, curses.ACS_VLINE) left = left + 1
def _handleEvent(self): entry = self._rootEntry + sums = [sum(values) for values in zip(self._first, self._selection)]
- for index in self._selection: + for index in sums: if isinstance(entry, ParentEntry): entry = entry.children[index] else: break
+ log.log(log.ERR, "first: %d" % self._first[0]) if isinstance(entry, LeafEntry): entry.callback(entry)
tor-commits@lists.torproject.org